User Tools

Site Tools


appunti3s:creare_classi_e_funzioni_in_python

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
appunti3s:creare_classi_e_funzioni_in_python [2018/10/18 21:55]
profpro
appunti3s:creare_classi_e_funzioni_in_python [2020/07/05 15:36] (current)
profpro [Terminologia]
Line 1: Line 1:
 +====== Creare classi e attributi ======
  
 +>> indice: [[appunti3s:linguaggio_python]]
 +
 +===== Introduzione =====
 +Questa introduzione andrebbe letta da chi conosce come si usano e si scrivono le funzioni, ma non conosce come si usano gli oggetti e le classi.
 +
 +Anche gli oggetti, come le funzioni, sono pezzi di programma che possono essere riutilizzati, ma a differenza delle tradizionali funzioni, questi trattano i dati in modo più "responsabile".
 +Immaginando che il programma sia un insegnante che interroga gli studenti, e che come prima cosa debba conoscere il nome dello studente: 
 +  * Un insegnante //orientato alle funzioni//, prenderebbe in mano il documento dello studente, per poter leggere il nome dal suo documento.
 +    * {{ :appunti3s:orientato_funzione.png?156 |}}
 +  * Un insegnante //orientato agli oggetti//, chiederebbe gentilmente allo studente di fornirgli il proprio nome.
 +    * {{ :appunti3s:orientato_oggetti.png?199 |}}
 +
 +Il primo insegnante, rispetto al secondo, agisce utilizzando un eccesso di forza e si comporta in modo sgarbato. Questo è il modo con cui si comporta una //tradizionale funzione// con tutti i dati. Infatti il programmatore può creare una funzione così forte che può modificare i parametri che gli vengono passati, questo perché non esiste nessun controllo che limita la volontà del programmatore.
 + Questo permette di scrivere rapidamente le funzioni nel programma, ma rende difficile scoprire eventuali errori presenti nel codice di una funzione, perché non esiste una sola funzione responsabile delle modifiche fatte su un certo dato.
 +
 +Il secondo insegnante pensa che gli studenti abbiano diritto alla loro privacy e che inoltre sia più sicuro chiedere il nome invece che sperare di trovare, chissà in quale tasca, un documento. Anche il programmatore, usando gli oggetti, assegna a loro delle responsabilità, e se c'è un errore nel funzionamento del programma, sa immediatamente dove trovare il responsabile. Perché, di solito, per ogni dato esiste un solo responsabile.
 +
 +Ci sono anche altri vantaggi nell'uso della programmazione orientata agli oggetti, come la facilità della manutenzione, dello sviluppo e del riutilizzo del codice, e altri che saranno compresi attraverso altri esempi.  
 +Per alcuni tipi di problemi la programmazione orientata agli oggetti invece non è la soluzione ideale....
 +Di solito l'approccio migliore per descrivere la soluzione di un problema dipende anche dal problema...
 + Un po' come quando alcuni problemi si risolvono meglio usando la ricorsione e altri usando l'iterazione.
 +
 +====Gli oggetti sono strumenti di lavoro====
 +
 +Gli oggetti sono strumenti che realizzano o forniscono qualcosa sui dati.
 +In questo modo si evita di occuparsi direttamente dei dati e si lascia che sia l'oggetto ad occuparsene.
 +
 +====Gli oggetti hanno una privacy====
 +
 +Gli oggetti sono responsabili dei propri dati, ma pretendono riservatezza. (vedere [[appunti3s:programmazione_orientata_agli_oggetti#data_hiding]]) Non si deve pretendere di sapere come un oggetto esegue il proprio lavoro, ma chiedere solo che produca il risultato aspettato. Non c'è bisogno di capire qualcosa quando non siamo noi ad occuparcene.
 +
 +>> Gli ogetti hanno una propria individualità, ma possono avere molti soprannomi...
 +
 +===== Terminologia =====
 +
 +La terminologia in Python è leggermente diversa da quella usata in Java o in C++
 +
 +  * __classe__ = una categoria, un modello, un tipo di dato usato per creare oggetti. Una classe crea un namespace che contiene dati e funzioni. In Python, al termine della def di una classe, viene crato un //wrapper// (un astratto "//oggetto-classe//") che descrive il contenuto di tale classe. 
 +    * __l'oggetto-classe__ viene indicato col nome della classe (maiuscolo) e dà accesso agli //attributi di classe// e ai //metodi di classe//. Si usa sempre la notazione con il punto.
 +      * un __attributo di classe__ è un dato appartenente all'oggetto-classe.
 +      * un __metodo di classe__ è un oggetto-funzione appartenente all'oggetto-classe.
 +  * __istanza__ = i comuni oggetti sono un'instanza di una classe, e si ottengono chiamando il nome della classe: ogg=NomeClasse(). Tale funzione NomeClasse() non esiste, ma chiama a sua volta la funzione di inizializzazione %%__init__()%%.
 +  * __attributi__ (dati e metodi) = tutto quello che viene dopo un punto (anche una funzione math.…).
 +    * __attributo dato__ = sono le variabili di un particolare oggetto istanza. Vivono nel namespace dell'oggetto istanza. Poiché in Python le variabili non si dichiarano in anticipo, nemmeno questi vengono dichiarati nella def della classe. Essi vengono dichiarati/inizializzati all'interno del costruttore %%__init__()%%.
 +    * __attributo metodo__ = sono le funzioni di un particolare oggetto istanza. Vivono nel namespace dell'oggetto istanza. Vengono definite dentro il namespace della classe e il loro primo attributo deve essere sempre //self//, il riferimento all'oggetto su cui sono state chiamate. Per la stessa ragione, quando in un metodo si chiama un altro medoto o si usa un //attributo dato//, si deve aggiungere il prefisso "//self.//attributo". Si può chiamare oggetto.metodo(), o equivalentemente, Classe.metodo(oggetto).
 +    * in Python è possibile rinominare i metodi (detti attributi metodi) perciò nella classe bisogna evitare conflitti di nome tra dati e funzioni. Si possono usare sostantivi per i primi e verbi per i secondi...
 +
 +Nota: //self// non è una keyword del linguaggio Python, ma il suo uso è una vecchia consuetudine e cambiarla sarebbe poco utile...
 +
 +Nota2: In Python, un attributo metodo è a sua volta manipolabile come un "oggetto", quindi possiede attributi.
 +===== Namespace =====
 + 
 +TO DO: Come si divide il codice in più file.py?
 +
 +La keyword //global// si usa per definire oggetti globali (meglio usarla solo per le funzioni)
 +
 +Come si possono definire nuovi namespace? Procedendo dal livello di accessibilità più esterno al più interno, possiamo definire un nuovo namespace usando un modulo, poi usando una classe e, infine, usando una funzione.
 +
 +Come si possono creare nuovi oggetti locali? Instanziando nuovi oggetti oppure usando import.
 +
 +Come si possono distruggere oggetti locali? Usando del.
 +
 +===== Visibilità =====
 +
 +Diversamente da altri linguaggi, come il C++, in Python **non** esistono specificatori di accesso (public,private,...) e tutti gli oggetti sono public. Si può suggerire al programmatore di evitare l'accesso diretto ad alcuni attributi (dati e metodi) usando una convenzione sul nome: aggiungendo un prefisso di dun underscore _ .
 +
 +===== Esempi =====
 +
 +Ricordate la suddivisione tra programmatori e sviluppatori? In questo momento dobbiamo immaginare di essere diventati degli sviluppatori che costruiscono classi e metodi perchè siano usate da altri programmatori!
 +
 +==== Esempio 1 ====
 +
 +Siamo degli **sviluppatori** e dobbiamo preparare le librerie che verranno usate dal programmatore in un programma che conterrà il catalogo di un negozio di auto usate. Si deve descrivere ogni caratteristica dei veicoli: targa, kilometraggio, prezzo,...
 +Queste descrizioni sono gli //attributi dato// che sono contenuti dentro //oggetti// nella memoria del computer. Tali oggetti sono creati tutti a partire dallo stesso modello: la classe Auto.
 +
 +Questa classe servirà al futuro **programmatore** per creare, ogni volta che serve, un oggetto auto. 
 +Il programmatore, per creare il primo oggetto può scrivere semplicemente:
 +<code>auto1 = Auto()</code>
 +Ora nella memoria del computer c'è una auto, ma è strana perché non si conosce nulla di quest'auto, come la targa, il prezzo, ecc.
 +Come può il futuro programmatore memorizzare la targa, il prezzo e i restanti attributi di questa auto? Li può specificare tra parentesi in questo modo:
 +<code>auto1 = Auto("AZ140WW", 100000, 9000000.00)</code>
 +
 +Ciò deve essere previsto dallo sviluppatore, realizzando quest'apposita funzione di inizializzazione dell'oggetto
 +<file python auto.py>
 +class Auto:
 +"""Questa classe definisce le caratteristiche di un auto in vendita"""
 +    def __init__(self, targa, km, prezzo):
 +        self.targa = targa
 +        self.km = km
 +        self.prezzo = prezzo
 +</file>
 +
 +%%__init__()%% è un //attributo metodo// (una funzione) che descrive gli //attributi dato// e la loro inizializzazione. È uno degli attributi metodo più importanti che lo sviluppatore deve preparare. Il programmatore lo usa (senza saperlo) ogni volta che crea un oggetto. 
 +Notare che mentre la chiamata del metodo contiene 3 parametri, nella funzione %%__init__()%% ce ne sono 4...
 +Notare anche l'ordine dei parametri tra parentesi. 
 +
 +==== Esempio 2 da terminare ====
 +
 +Se vogliamo scrivere un programma per una banca che possa gestire i dati anagrafici dei clienti e i dati dei loro conti correnti, ci vorranno come minimo due classi: Cliente e ContoCorrente.
 +Queste due classi serviranno al programmatore per creare, ogni volta che serve, un oggetto cliente e un oggetto contocorrente. Dentro questi oggetti sarà possibile trovare i dati come nome e cognome del cliente e numero e saldo del conto corrente. Per ora trascuriamo il problema di conoscere a chi è intestato il conto...
 +
 +Questi oggetti possono essere considerate delle strutture dove memorizzare i dati.
 +
 +Il problema successivo è il seguente: come operare sui dati? Si può agire in due modi:
 +  * lasciare operare il programmatore direttamente sui dati per mezzo di istruzioni di assegnazione, come conto.saldo += 40.00
 +  * oppure, creare dei metodi che operino in sicurezza sui dati, tutte le volte che serve al programmatore, come conto.versamento(40.00)
 +
 +La programmazione orientata agli oggetti di solito persegue la seconda strategia, e consiglia di nascondere i dati al programmatore (data hiding) per evitare che vi acceda direttamente (combinando qualche guaio). In Python non è possibile modificare la visibilità degli attributi degli oggetti, si può solo agire sul loro nome, aggiungendo due underscore _ .
 +
 +Quindi nella classe devo avere:
 +  * attributi dato (ma non si dichiarano)
 +  * attributi metodo: def dell'inizializzatore %%__init()__%% e di altri metodi 
 +<code>class ContoCorrente:
 +    __numero_conto=int(0)
 +    __saldo=float(0)
 +    def __init__(self):
 +        __numero_conto=int(0)
 +        __saldo=float(0)
 +    def versamento(self, importo):
 +        self.__saldo+=float(importo)
 +        return __saldo
 +</code>
 +<file python bind01.py>
 +#!/usr/bin/python
 +
 +class ContoCorrente:
 +"""questa classe e' solo un esempio didattico"""
 +# gli attributi vanno cercati, perche' si trovano sparsi dentro i metodi
 +# ad esempio , due attributi sono i due pulsanti
 +
 +    def __init__(self, master):
 +                              
 +        frame = Frame(master, bg='white', width=200, height=200) 
 +                          # container bianco # e' una var. locale di una funzione 
 +                          # non viene distrutta solo grazie ad un sistema di Tkinter 
 +                          # che la mantiene in vita...
 +        frame.bind("<Button-1>", self.say_click) # evento del click
 +        frame.pack() # deve essere l'ultima cosa da fare su frame?
 +
 +    def say_click(self, event):       # metodo di app
 +        print "clicked at", event.x, event.y     
 +
 +# programma 
 +
 +root = Tk()                # crea la finestra genitore
 +root.geometry("250x250+300+300")
 +
 +app = App(root) # crea altri elementi della finestra genitore
 +
 +root.mainloop() # loop degli eventi eseguito fino alla chiusura
 +
 +</file>
 +
 +
 +
 +==== Esempio 3 da terminare ====
 +<code>import tkFileDialog
 +#creare prima una finestra e usare questo metodo per aprire una finestra di dialogo
 +percorso = tkFileDialog.askopenfilename
 +</code>
 +Applicazione con event
 +
 +<file python bind01.py>
 +#!/usr/bin/python
 +from Tkinter import *
 +
 +# definire una classe App (di solito una sottoclasse)
 +# permette di personalizzare la finestra
 +
 +class App:
 +"""questa classe e' solo un esempio didattico"""
 +# gli attributi vanno cercati, perche' si trovano sparsi dentro i metodi
 +# ad esempio , due attributi sono i due pulsanti
 +
 +    def __init__(self, master):
 +                              
 +        frame = Frame(master, bg='white', width=200, height=200) 
 +                          # attributo dato: un container bianco 
 +
 +        frame.bind("<Button-1>", self.say_click) # evento del click
 +        frame.pack() # deve essere l'ultima cosa da fare su frame?
 +
 +    def say_click(self, event):       # metodo di app
 +        print "clicked at", event.x, event.y     
 +
 +# programma 
 +
 +root = Tk()                # crea la finestra genitore
 +root.geometry("250x250+300+300")
 +
 +app = App(root) # crea altri elementi della finestra genitore
 +
 +root.mainloop() # loop degli eventi eseguito fino alla chiusura
 +
 +</file>
 +
 +Se inserisco due pulsanti nel frame, il frame si stringe e diventa invisibile...
 +
 +Applicazione a finestra con costruttore personalizzato.
 +Tk ha il metodo destroy()?? Frame ha il metodo quit()??
 +
 +<file python hello2.py>
 +#!/usr/bin/python
 +from Tkinter import *
 +
 +# definire una classe App (di solito una sottoclasse)
 +# permette di personalizzare la finestra
 +
 +class App:
 +"""questa classe e' solo un esempio didattico"""
 +# gli attributi vanno cercati, perche' si trovano sparsi dentro i metodi
 +# ad esempio , due attributi sono i due pulsanti
 +
 +    def __init__(self, master):
 +                              
 +        frame = Frame(master, bg='white', width=200, height=200) 
 +                          # container ora invisibile per posizionare pulsanti
 +                          # e' una var. locale di una funzione (come button) 
 +                          # non viene distrutta solo grazie ad un sistema di Tkinter 
 +                          # che la mantiene in vita...
 +
 +        frame.bind("<Button-1>", self.say_click) # evento del click
 +        frame.pack() # deve essere l'ultima cosa da fare su frame...
 +        
 +        self.buttonQuit = Button(frame, text="Quit", fg="red", command=frame.quit)
 +        self.buttonQuit.pack(side=LEFT)
 +        self.buttonHello = Button(frame, text="Hello", command=self.say_hello)
 +        self.buttonHello.pack(side=LEFT)
 +
 +    def say_hello(self):       # metodo di app
 +        print "hello in the terminal!"
 +
 +# programma 
 +
 +root = Tk()                # crea la finestra genitore
 +root.geometry("250x250+300+300")
 +
 +app = App(root) # crea altri elementi della finestra genitore
 +
 +root.mainloop() # loop degli eventi eseguito fino alla chiusura
 +
 +</file>