Per create un'istanza di una classe possiamo usare il nome della classe come fosse una funzione:
class test: .... x = test()
Questo crea una nuova istanza che contiene una copia del namespace della classe
Questo namespace contiene inizialmente tutti i metodi e gli attributi definiti nella classe
Possiamo aggiungere attributi e metodi all'istanza in ogni momento
Questi attributi e metodi saranno della sola istanza.
Attributi
Come già per le variabili, gli attributi vengono definiti nel momento in cui gli si assegna un valore
Gli attributi definiti all'interno della classe e fuori dai metodi sono nel contesto della classe
Naturalmente verranno duplicati nel namespace delle istanze, quini ne avremo sempre due copie indipendenti: una <classe>.<attributo>statica ed una self.<attributo> associata all'istanza
Se invece all'interno di un metodo definiamo un attributo come self.<attributo>, esso sarà esclusivamente associato all'istanza
Attributi
Per quanto riguarda gli attributi statici usati nella loro copia di istanza, occorre porre attenzione ai tipi mutabili
Se noi abbiamo un attributo statico che si riferisce, ad esempio, una lista, l'attributo avrà una copia di istanza, che però si riferirà alla stessa lista
Se in questo caso modifichiamo la lista tramite un metodo di lista (quindi non ne sostituiamo il riferimento), la modifica avrà effetto globale.
Costruttore
Anche in Python è previsto un costruttore ha il nome standard __init__(self[,<parametro>]...)
self è il parametro obbligatorio che si riferirà all'istanza
È possibile indicare parametri per il costruttore, eventualmente anche con dei valori di default
Non è invece possibile definire più costruttori con parametri diversi, in quanto il secondo costruttore sovrascriverebbe il primo e ne avremmo comunque uno solo
Costruttore
La gestione della memoria in Python è automatica, quindi non c'è necessità di distruggere un'istanza, per recuperare memoria
Nel caso comunque sia necessario effettuare qualche operazione, come chiudere un file prima che un'istanza venga eliminata dalla memoria, esiste il metodo __del__(self)
Metodi
I metodi vengono definiti come normali funzioni, ma all'interno della classe.
I metodi della classe devono avere tutti almeno un parametro (il primo), generalmente chiamato self, che si riferisce all'istanza su cui vengono chiamati
Anche se il metodo non utilizza self, occorre indicarlo perché Python lo passa ad ogni metodo invocato su di un'istanza
Per accedere ad ogni attributo o metodo dell'istanza siamo obbligati a passare attraverso self.
Metodi
Se creiamo nella classe un metodo privo del parametro self, non potremo invocarlo su di un'istanza e lo potremmo considerare come un metodo statico
È sempre possibile invocare un metodo tramite la classe, aggiungendo esplicitamente il primo parametro con l'istanza.
Definizioni di classi
Per definire una classe usiamo la parola chiave class: class test:
La classe è inclusa in un blocco, quindi dovremo utilizzare un livello di indentazione
La definizione della classe implica la definizione di un nuovo namespace
Gli attributi globali ed i metodi verranno inseriti in questo namespace
Sia attributi che metodi sono pubblici. Non possiamo fare altrimenti
Definizioni di classi
Nel namespace finiscono solo i nomi dei metodi, quindi non è possibile sovraccaricare i metodi
Namespaces
Prima di addentrarci nelle classi, meglio dare un'occhiata al concetto di namespace ed al suo uso in Python
Un namespace è una collezione di nomi, associati ai relativi oggetti (inteso in senso lato - funzioni, variabili, ecc.)
Un nome può comparire in più namespace. Si riferirà a oggetti diversi
Se uso un nome di variabile o funzione, mi riferisco al default namespace che contiene le variabili e le funzioni definite nel mio programma ed una serie di variabili e funzioni di sistema
Namespaces
Se carico un modulo, come abbiamo fatto con random o simplegui, esso si porta dietro un suo namespace
È per questo che quando usiamo i metodi di random o di simplegui dobbiamo sempre indicare il nome del modulo prima, come in random.randrange()
Esercizio
Modificare l'esercizio del cielo stellato:
creare una classe stella, che contenga le coordinate, una variabile periodo ed una momento
la stella avrà un costruttore che riceverà la x., la y e il periodo
la stella avrà un metodo di disegno, che riceverà il pannello
ad ogni ridisegno, la stella incrementerà il "momento" fino a raggiungere periodo
la stella crescerà da 1 a cinque pixel per la prima parte del periodo, per poi calare di nuovo fino alla fine del periodo stesso
Ereditarietà
In Python è implementata l'ereditarietà, anche quella multipla
Viene implementata clonando ed unendo i namespace delle classi base
Per indicare le superclassi basta metterle tra parentesi, dopo il nome della classe: class figlio(padre, madre):
Visti i ragionamenti precedenti, in questo modo viene garantita l'ereditarietà e la possibilità di ridefinire metodi
Ereditarietà
Per chiamare metodi della superclasse, dobbiamo farlo in maniera esplicita, tramite la classe:
class figlio(padre): def metodo(self,x): padre.metodo(self,x)
Per verificare il tipo di una istanza possiamo utilizzare is:
class c: pass v = c() print type(v) is c
Ma questo non ci dice se la nostra istanza discende da una certa classe.
Ereditarietà
Se vogliamo verificare la discendenza, useremo isinstance(<istanza>,<classe>):
class padre: pass class figlio(padre): pass v = figlio() print isinstance(v, padre)
Secondo Esercizio
Modificare l'esercizio della pallina e racchetta:
creare una classe oggetto che contenga le coordinate X ed Y nonché larghezza ed altezza
oggetto implementerà un metodo scontro che riceve come parametro un altro oggetto
implementerà anche un metodo disegno, che riceve il panel come parametro
creare una classe palla derivata da oggetto che visualizza la pallina
creare una classe racchetta che implementa la racchetta
riscrivere il metodo di disegno utilizzando le nuove classi
Sovrapposizione degli operatori
In Python è implementata una rudimentale forma di sovrapposizione degli operatori
Quanto noi eseguiamo operazioni tra istanze di classe vengono invocati dei metodi particolari, chiamati magic methods
Sono facilmente riconoscibili perché il loro nome è sempre racchiuso tra __ e __, come __init__ e __del__
Ad esempio, se vogliamo implementare i confronti tra classi possiamo implementare il metodo __cmp__(self,other), che ritornerà un valore intero come strcmp()
Sovrapposizione degli operatori
Esistono altre magic funzion per implementare i singoli tipi di confronto, se necessario
È anche possibile ridefinire le operazioni matematiche con __add__ __sub__ __mul__ __div__ ecc...
È possibile anche emulare una lista o implementare un rudimentale sistema di incapsulamento degli attributi, tramite le magic function