Classi, istanze e metodi
Conosci te stesso.
Nella lezione precedente ti avevo chiesto di notare una cosa sola: i dati di un brano e ciò che sai farci vivono nello stesso blocco. Avevamo scritto una classe Brano e l’avevamo guardata da lontano, come si guarda un motore acceso senza aprire il cofano.
Oggi apriamo il cofano. Quella parola class, quel self che spuntava ovunque, quel __init__ dall’aria criptica: smettono di essere magia e diventano tre meccanismi precisi che, una volta capiti, userai per il resto del volume. E lo facciamo continuando proprio da Brano, dandogli più comportamenti e affiancandogli la sua compagna naturale: la Playlist di Risonanza.
Si riparte da Brano
Eccola di nuovo, la classe della scorsa lezione. Premi Run: non è cambiato niente, è il nostro punto di partenza.
Cinque righe di definizione, e dopo possiamo creare quanti brani vogliamo. Ma come funziona? Tre domande, tre sezioni: __init__, self, e la coppia attributi/metodi.
__init__: il costruttore
Quando scrivi Brano("Bohemian Rhapsody", "Queen", 354), stai chiedendo a Python di costruire un nuovo oggetto. Ma un oggetto appena nato è una scatola vuota: come fa a sapere che il suo titolo è Bohemian Rhapsody e la sua durata 354 secondi? Glielo dice il .
__init__ (doppio underscore, init, doppio underscore — i programmatori lo pronunciano dunder init) è un metodo speciale con un solo compito: inizializzare lo stato di un oggetto appena creato. Non lo chiami mai tu esplicitamente — è Python a invocarlo, in automatico, ogni volta che istanzi la classe.
Vediamolo all’opera. Ho infilato un print dentro __init__: comparirà esattamente una volta per ogni brano che creo.
Tre oggetti creati, tre stampe: __init__ è scattato tre volte, una per brano. Nessuno l’ha chiamato a mano — è il prezzo che Python paga in automatico ogni volta che apre la parentesi su Brano(...).
self: l’oggetto che conosce sé stesso
Hai notato che il primo parametro di __init__ — e, vedremo, di ogni metodo — è self? Eppure quando crei un brano passi tre argomenti, non quattro:
bohemian = Brano("Bohemian Rhapsody", "Queen", 354) # 3 argomenti, non 4
Dove finisce self? Non lo passi tu: lo passa Python. self è il riferimento all’oggetto specifico che si sta costruendo o usando in quel momento. Dentro __init__, quando scrivi self.titolo = titolo, stai dicendo: “attacca questo titolo a questo brano qui, non a un altro”.
È letteralmente l’oggetto che conosce sé stesso — da cui la citazione in cima. Ogni self.qualcosa è un dato che l’oggetto porta con sé e a cui può sempre accedere.
La prova che ogni oggetto ha il suo self, indipendente dagli altri:
Dentro riproduci, self è bohemian quando lo chiami su bohemian, ed è bury quando lo chiami su bury. Per questo il contatore di uno non tocca quello dell’altro: self.riproduzioni += 1 agisce sempre e solo sull’oggetto su cui hai chiamato il metodo.
In altri linguaggi (Java, C++, JavaScript) questo riferimento esiste ma è implicito e si chiama this. Python ha scelto di renderlo esplicito: explicit is better than implicit, recita lo Zen of Python. Discutibile, ma è così, e dopo un po’ ci si affeziona.