Python è completamente orientato agli oggetti: potete
definire le vostre classi, ereditare dalle vostre classi
o da quelle built-in ed istanziare le classi che avete
definito.
Definire una classe in Python è semplice; come per le
funzioni, non c'è una definizione separata per le interfacce.
Semplicemente definite la classe e cominciate a programmare.
Una classe in Python inizia con la parola riservata
class, seguita dal nome della classe. Tecnicamente, questo
è tutto ciò che è richiesto, visto che una classe non deve
necessariamente essere ereditata da un'altra.
Esempio 4.5. La più semplice classe Python
class foo:
pass
|
Il nome di questa classe è foo e non
è ereditata da nessun'altra classe.
|
|
Questa classe non definisce nessun metodo o attributo ma
sintatticamente, c'è bisogno di qualcosa nella definizione,
quindi usiamo pass. È una parola riservata in Python,
che significa semplicemente “va avanti, non c'è nulla
da vedere qui”. È un'istruzione che non fa
niente, ed è un buon segnaposto quando state abbozzando
funzioni o classi.
|
|
Probabilmente lo avete già indovinato, ogni cosa in una
classe è indentato, proprio come il codice in una funzione,
l'istruzione if, il ciclo for e così via. La prima cosa
non indentata non è nella classe.
|
|
Lo statement pass in Python è come un paio di graffe vuote
({}) in Java od in C.
|
Ovvio che, realisticamente, molte classi saranno ereditate
da altre classi e definiranno i loro specifici metodi ed
attributi. Ma come avete appena visto, non c'è nulla che
una classe debba assolutamente avere, salvo il nome. In
particolare, i programmatori C++ troveranno bizzarro il
fatto che le classi Python non hanno esplicitamente
costruttori e distruttori. Le classi Python hanno qualcosa
di simile ad un costruttore: il metodo __init__.
Esempio 4.6. Definire la classe FileInfo
from UserDict import UserDict
class FileInfo(UserDict):
|
In Python l'antenato di una classe è semplicemente elencato
tra parentesi subito dopo il nome della classe. Così la
classe FileInfo è ereditata dalla
classe UserDict (che è stata
importata dal
modulo UserDict).
UserDict è una classe che agisce come
un dizionario, permettendovi essenzialmente di derivare una
classe da quel tipo dizionario ed aggiungere il vostro
specifico comportamento.
(Ci sono similmente le classi UserList
e UserString che vi permettono di
derivare classi per liste e stringhe). Dietro questo
comportamento c'è un po' di magia nera, la sfateremo
più tardi in questo capitolo, quando esploreremo in
maggior dettaglio la classe UserDict.
|
|
In Python, l'antenato di una classe è semplicemente elencato
tra parentesi subito dopo il nome della classe. Non c'è alcuna
keyword come la extends di Java.
|
|
Anche se non la discuterò in profondità nel libro, Python
supporta l'ereditarietà multipla. Nelle parentesi che seguono
il nome della classe, potete elencare quanti antenati volete,
separati da virgole.
|
Esempio 4.7. Inizializzazione della classe FileInfo
class FileInfo(UserDict):
"store file metadata"
def __init__(self, filename=None):
|
Anche le classi possono (e
dovrebbero) avere le
docstrings, proprio come i moduli e le funzioni.
|
|
__init__ è chiamato immediatamente dopo la creazione
dell'istanza di una classe. Si può essere erroneamente
tentati nel chiamare questo metodo il costruttore della
classe. Tentati, perché sembra proprio il costruttore
(per convenzione, __init__ è il primo metodo definito nella
classe), agisce come un costruttore (è il primo pezzo
di codice eseguito in una nuova istanza di una classe)
e suona come un costruttore (“init” certamente
suggerisce una natura costruttiva). Erroneamente, perché
l'oggetto è già stato costruito prima che __init__ venga
chiamato ed avete già un riferimento valido alla nuova
istanza della classe. Ma __init__ è la cosa più vicina ad un
costruttore che incontrerete in Python e ricopre
essenzialmente il medesimo ruolo.
|
|
Il primo argomento di ogni metodo di una classe, incluso
__init__, è sempre un riferimento all'istanza corrente della
classe. Per convenzione, questo argomento viene sempre
chiamato self. Nel metodo __init__, self si riferisce
all'oggetto appena creato; negli altri metodi della classe,
si riferisce all'istanza da cui metodo è stato chiamato. Per
quanto necessitiate di specificare self esplicitamente
quando definite un metodo, non lo
specificate quando chiamate il metodo; Python lo
aggiungerà per voi automaticamente.
|
|
Il metodo __init__ può prendere un numero arbitrario di
argomenti e proprio come per le funzioni, gli argomenti
possono essere definiti con valori predefiniti, rendendoli
opzionali per il chiamante. In questo caso,
filename ha il valore predefinito None,
che è il valore nullo di Python.
|
|
Per convenzione, il primo argomento di ogni metodo di una classe (il
riferimento all'istanza corrente) viene chiamato self. Questo argomento
ricopre il ruolo della parola riservata this in C++ o Java, ma self
non è una parola riservata in Python, è semplicemente una convenzione sui
nomi. Non di meno, vi prego di non chiamarlo diversamente da self; è una
convenzione molto forte.
|
Esempio 4.8. Realizzare la classe FileInfo
class FileInfo(UserDict):
"store file metadata"
def __init__(self, filename=None):
UserDict.__init__(self)
self["name"] = filename
|
Alcuni linguaggi pseudo-orientati agli oggetti come Powerbuilder hanno un
concetto di “estensione” dei costruttori ed altri eventi, dove
il metodo dell'antenato è chiamato automaticamente prima che il metodo del
discendente venga eseguito. Python non fa questo; dovete sempre chiamare
esplicitamente il metodo appropriato della classe antenata.
|
|
Vi ho detto che questa classe agisce come un dizionario e questo ne è il
primo segno. Stiamo assegnando l'argomento filename come
valore della chiave name di questo stesso oggetto.
|
|
Notate che il metodo __init__ non ritorna mai un valore.
|
|
Quando definite i vostri metodi nella classe,
dovete elencare esplicitamente self
come il primo argomento di ogni metodo, incluso __init__.
Quando chiamate il metodo di una classe antenata dall'interno
della vostra classe, dovete includere
l'argomento self. Invece, quando chiamate i metodi della vostra
classe dall'esterno, non dovete specificare affatto
l'argomento self, lo saltate per intero e Python
automaticamente aggiunge il riferimento all'istanza per voi.
Temo che inizialmente possa confondere; non è proprio
inconsistente ma può sembrarlo perché fa
affidamento su una distinzione (tra metodi bound ed unbound)
che ancora non conoscete.
|
Wow. Capisco che è un bel po' di roba da comprendere, ma ne
verrete fuori. Tutte le classi Python funzionano allo
stesso modo, così una volta che ne avrete imparata una,
avrete imparato tutto. Se vi dimenticate tutto il resto,
ricordate questa cosa, perché vi prometto che vi aiuterà:
|
i metodi __init__ sono opzionali, ma quando ne definite uno, dovete
ricordarvi di chiamare esplicitamente il metodo __init__
dell'antenato. Questo è generalmente vero; quando un
discendente vuole estendere il comportamento di un antenato,
il metodo del discendente deve esplicitamente chiamare il
metodo dell'antenato nel momento opportuno e con gli
opportuni argomenti.
|