lettura facile

Le classi super in Python

Oggi parliamo delle "classi super" in Python. Sono classi che ti permettono di chiamare  i metodi dalla classe base, rendendo più semplice l'ereditarietà e il riutilizzo del codice tra le classi derivate.

super()

Una "classe super" si ottiene utilizzando la funzione super(), che è essenziale per gestire l'ereditarietà tra classi  Questo ti permette di creare una nuova classe derivata che eredita attributi e metodi da una classe esistente, chiamata classe base.

La funzione super() restituisce un oggetto temporaneo della classe parente e ti consente di chiamare i suoi metodi direttamente dalla classe derivata.

Perché usare super()? E' particolarmente utile in una gerarchia di ereditarietà, dove potresti voler estendere o modificare il comportamento di un metodo della classe base nella classe derivata. Ad esempio, se cambi un metodo nella classe base, le modifiche vengono automaticamente propagate alle classi derivate. Inoltre, puoi richiamare un metodo della classe base senza dover esplicitamente nominare la classe base, il che è utile per mantenere il codice flessibile e ridurre la dipendenza da nomi specifici di classi.

Facciamo un esempio pratico.

Immagina di avere una classe base `Animale` e una classe derivata `Cane` e vuoi che la classe `Cane` estenda alcune funzionalità della classe `Animale`. Puoi farlo usando la funzione super() .

class Animale:
   def __init__(self, nome):
      self.nome = nome

   def parla(self):
      raise NotImplementedError("Questo metodo deve essere implementato dalla sottoclasse")

class Cane(Animale):
    def __init__(self, nome, razza):
       super().__init__(nome) # Chiama il costruttore della classe base
       self.razza = razza

    def parla(self):
       return f"{self.nome} dice: Bau Bau!"

In questo esempio la classe base `Animale` definisce un costruttore (`__init__`) che prende il nome dell'animale e un metodo `parla` che solleva un'eccezione. Questo metodo è pensato appositamente per essere sovrascritto nelle classi derivate.

La classe derivata `Cane` definisce un costruttore che prende sia il nome che la razza del cane e usa la funzione `super().__init__(nome)` per inizializzare l'attributo `nome` con il costruttore della classe base. Inoltre, sovrascrive il metodo `parla` per fornire un'implementazione specifica per i cani.

Nota che non è necessario indicare il nome della classe base nella funzione super(), perché Python la ricava automaticamente dall'attributo '__base__' dell'oggetto. Questa semplificazione è stata apportata con la versione Python3. Se invece stai usando la versione precedente Python2, il nome della classe base va indicato tra le parentesi della funzione super().

Ad esempio, crea un'istanza della classe Cane.

mio_cane = Cane("Fido", "Labrador")

Ora interroga il metodo parla() dall'istanza 'mio_cane'

print(mio_cane.parla())

Fido dice: Bau Bau!

L'oggetto ha ereditato l'attributo 'nome' dalla classe base e ha sovrascritto il metodo parla().

In questo esempio hai usato la funzione super() nella classe 'Cane' per richiamare l'attributo 'nome' dal costruttore della classe base ('Animale').

class Cane(Animale):
    def __init__(self, nome, razza):
       super().__init__(nome) # Chiama il costruttore della classe base
       self.razza = razza

    def parla(self):
       return f"{self.nome} dice: Bau Bau!"

Avresti potuto ottenere lo stesso risultato anche senza usare la funzione super(), indicando il nome della classe madre in modo esplicito. In questo caso avresti dovuto indicare 'Animale'. Questo approccio, però, è meno flessibile e può essere più difficile da mantenere, specialmente in progetti più complessi o quando si utilizza l'ereditarietà multipla.

class Cane(Animale):
    def __init__(self, nome, razza):
       Animale.__init__(nome) # Chiama il costruttore della classe base
       self.razza = razza

    def parla(self):
       return f"{self.nome} dice: Bau Bau!"

L'ereditarietà multipla con le classi super

L'uso di `super()` diventa ancora più importante nelle classi multiple e nell'ereditarietà multipla.

Vediamo un esempio più complesso con l'ereditarietà multipla:

class A:
   def metodo(self):
     print("Metodo di A")

class B(A):
   def metodo(self):
     print("Metodo di B")
     super().metodo() # Chiama il metodo di A

class C(A):
   def metodo(self):
     print("Metodo di C")
     super().metodo() # Chiama il metodo di A

class D(B, C):
   def metodo(self):
     print("Metodo di D")
     super().metodo() # Chiama il metodo di B e poi di C

In questo esempio la Classe `A` definisce un semplice metodo che stampa una stringa.

Le classi `B` e `C` ereditano da `A` e sovrascrivono il metodo `metodo`, chiamando il metodo della classe base tramite la funzione super().

La classe `D` eredita sia da `B` che da `C`. In questo caso `super().metodo()` segue l'ordine di risoluzione del metodo (MRO - Method Resolution Order), chiamando prima `metodo` di `B` e poi `metodo` di `C`, e così via.

Ad esempio, crea un'istanza della classe D

d = D()

Poi interroga il metodo dell'istanza.

d.metodo()

Python segue l'MRO per determinare l'ordine delle chiamate e l'output del programma sarà:

Metodo di D
Metodo di B
Metodo di C
Metodo di A

In conclusione, l'uso di `super()` è fondamentale per gestire in modo efficace l'ereditarietà in Python, in quanto rende il codice più manutenibile e flessibile.

Capire come e quando utilizzare `super()` ti permetterà di scrivere classi più robuste e riutilizzabili.




Se qualcosa non ti è chiaro, scrivi la tua domanda nei commenti.




FacebookTwitterLinkedinLinkedin