4.3. Definire classi

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: 1
    pass   2 3
1 Il nome di questa classe è foo e non è ereditata da nessun'altra classe.
2 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.
3 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.
Nota
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): 1
1 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.
Nota
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.
Nota
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"              1
    def __init__(self, filename=None): 2 3 4
1 Anche le classi possono (e dovrebbero) avere le doc strings, proprio come i moduli e le funzioni.
2 __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.
3 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.
4 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.
Nota
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)        1
        self["name"] = filename        2
                                       3
1 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.
2 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.
3 Notate che il metodo __init__ non ritorna mai un valore.
Nota
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à:

Nota
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.

Ulteriori letture