4.4. Istanziare classi

Istanziare una classe in Python è molto semplice. Per istanziare una classe, basta semplicemente chiamare la classe come se fosse una funzione, passandole gli argomenti che il metodo __init__ definisce. Il valore di ritorno sarà il nuovo oggetto.

Esempio 4.9. Creare un'istanza di FileInfo

>>> import fileinfo
>>> f = fileinfo.FileInfo("/music/_singles/kairo.mp3") 1
>>> f.__class__                                        2
<class fileinfo.FileInfo at 010EC204>
>>> f.__doc__                                          3
'base class for file info'
>>> f                                                  4
{'name': '/music/_singles/kairo.mp3'}
1 Stiamo creando un'istanza della classe FileInfo (definita nel modulo fileinfo) ed assegnando l'istanza appena creata alla variabile f. Stiamo passando un parametro, /music/_singles/kairo.mp3, che andrà a finire nell'argomento filename del metodo __init__ di FileInfo.
2 Ogni istanza di classe ha un attributo built-in, __class__, che è la classe dell'oggetto. (Notate che la sua rappresentazione include l'indirizzo fisico dell'istanza nella mia macchina; la vostra rappresentazione sarà diversa.) I programmatori Java potranno già avere familiarità con la classe Class, che contiene metodi come getName e getSuperclass per ottenere le informazioni sui metadati di un oggetto. In Python, questo tipo di metadato è disponibile direttamente nell'oggetto stesso attraverso attributi come __class__, __name__ e __bases__.
3 Potete accedere alla docstring di un'istanza proprio come una funzione o un metodo. Tutte le istanze di una classe condividono la medesima docstring.
4 Ricordate quando il metodo __init__ assegnava il suo argomento filename a self["name"]? Beh, questo è il risultato. Gli argomenti che noi passiamo quando creiamo l'istanza della classe vengono mandati direttamente al metodo __init__ (assieme al riferimento all'oggetto, self, che Python aggiunge gratuitamente).
Nota
In Python, semplicemente chiamate una classe come se fosse una funzione per creare una sua nuova istanza. Non c'è alcun operatore esplicito new come in C++ o in Java.

Se creare nuove istanze è facile, distruggerle lo è ancora di più. In generale, non c'è alcun bisogno di liberare esplicitamente delle istanze, in quanto vengono automaticamente liberate quando la variabile ad esse assegnata esce dallo scope. I memory leaks sono rari in Python.

Esempio 4.10. Proviamo ad implementare un memory leak

>>> def leakmem():
...     f = fileinfo.FileInfo('/music/_singles/kairo.mp3') 1
...     
>>> for i in range(100):
...     leakmem()                                          2
1 Ogni volta che la funzione leakmem viene chiamata, creiamo un'istanza di FileInfo e la assegnamo alla variabile f, che è una variabile locale alla funzione. Quindi la funzione termina senza liberare f, vi aspettereste un memory leak, ma sareste in errore. Quando la funzione termina, la variabile locale f esce dal suo scope. A questo punto, non c'è più alcun riferimento alla nuova istanza di FileInfo (in quanto non l'abbiamo mai assegnata ad altra variabile, tranne a f), così Python distrugge l'istanza per noi.
2 Non importa quante volte chiamate la funzione leakmem, questa non perderà mai della memoria perché ogni volta, Python distruggerà la nuova istanza di FileInfo prima di ritornare da leakmem.

Il termine tecnico per questo genere di garbage collection è “reference counting”. Python tiene una lista dei riferimenti ad ogni istanza creata. Nell'esempio precedente, c'era un solo riferimento all'istanza di FileInfo: la variabile locale f. Quando la funzione termina, la variabile f esce dallo scope, così il contatore dei riferimenti finisce a 0 e Python distrugge l'istanza automaticamente.

Nelle precedenti versioni di Python vi erano situazioni in cui il reference counting falliva e Python non era in grado di ripulire la memoria. Se creavate due istanze che si referenziavano a vicenda (per esempio, una lista doppiamente linkata, dove ogni nodo ha un puntatore al prossimo nodo ed al precedente), nessuna istanza sarebbe stata distrutta automaticamente perché Python (correttamente) credeva che ci fosse sempre un riferimento ad ogni istanza. Python 2.0 ha una forma addizionale di garbage collection chiamata “mark-and-sweep” che è sufficientemente intelligente da notare questo cappio virtuale e quindi, ripulire correttamente i riferimenti circolari.

In qualità di filosofo mi disturba l'idea che le cose scompaiano quando nessuno le sta guardando, ma questo è quanto accade in Python. In generale, potete semplicemente dimenticarvi della gestione della memoria e lasciare che sia Python a ripulire il vostro lavoro.

Ulteriori letture