Pratiche comuni e criticità dell’ereditarietà in Python problema del diamante, mixin e altri

Pratiche comuni e criticità dell'ereditarietà in Python il problema del diamante, i mixin e altro

L’eredità, come ogni altro concetto in OOPs, consente agli sviluppatori di riutilizzare il codice e sviluppare soluzioni software eleganti e scalabili. Tenendo presente l’ampia comunità di Python sviluppati da moduli e logiche multiple. Tuttavia, come con qualsiasi concetto, l’eredità ha alcune trappole comuni, come il problema Diamond, le sfide dell’ereditarietà delle interfacce e l’uso dei Mixin.

Immagine dell'autore

L’eredità è un concetto fondamentale nella programmazione orientata agli oggetti, in cui una nuova classe eredita la classe del genitore e riutilizza tutti i metodi e le variabili definiti nella classe del genitore. Pertanto, promuove la riutilizzabilità del codice, la scalabilità e la modularità.

Ereditarietà multipla

L’ereditarietà multipla consente a una classe di ereditare attributi e metodi da più di una classe base. Ciò significa che una classe derivata può ereditare contemporaneamente più classi.

class volare:  can_fly = Trueclass nuotare:  can_swim = Trueclass Anatra(nuotare, volare):    passd = Anatra()print(d.can_fly)   #Trueprint(d.can_swim)  #True

Quello sopra è un semplice esempio di ereditarietà multipla e di come possa essere utile, se vogliamo scrivere un programma Python sugli uccelli e menzionare alcune specialità indipendentemente nelle classi e vogliamo riutilizzarle quando si parla di un particolare uccello. Nel caso dell’istanza di Anatra, che può nuotare, volare e anche camminare. In questi casi, l’ereditarietà multipla è utile.

Ci sono linguaggi di programmazione che non supportano l’ereditarietà multipla, anche se ci fornisce flessibilità nel riutilizzo del codice. Può introdurre nuove sfide come il problema Diamond in cui può sorgere una confusione quando una classe eredita da più classi con una base comune.

Problema Diamond

Il problema Diamond si verifica in particolare quando una classe figlia eredita da più classi genitore che hanno una base comune. Questo crea ambiguità nell’ordine in cui i metodi sovrascritti devono essere eseguiti. Vediamo l’esempio seguente:

class Animale:    def parlare(self):        print("L'animale parla")class Uccello(Animale):    def parlare(self):        print("L'uccello cinguetta")class Pesce(Animale):    def parlare(self):        print("Il pesce bolle")class Anfibio(Uccello, Pesce):    pass

In questa classe Anfibio, eredita sia dalla classe Pesce che dall’uccello che hanno una base comune Animale. Poiché il metodo parlare viene sovrascritto in entrambe le classi genitore, viene creata confusione per la classe Anfibio per invocare un metodo quando viene chiamato il metodo parlare. Per questa ragione, alcuni linguaggi di programmazione non supportano l’ereditarietà multipla.

Ma Python, con l’aiuto della Linearizzazione C3, affronta il problema Diamond utilizzando l’Ordine di Risoluzione dei Metodi (MRO) e consente l’ereditarietà.

Ordine di Risoluzione dei Metodi (MRO)

In Python, la linearizzazione C3, introdotta nella versione 2.3, governa l’Ordine di Risoluzione dei Metodi. Determina l’ordine in cui le classi devono essere cercate per trovare un metodo o una variabile appropriata. Gli algoritmi di linearizzazione C3 tengono conto dell’ordine dell’ereditarietà e derivano la relazione tra le classi.

Quando viene chiamato un metodo, Python inizia cercandolo nella classe corrente e, se non lo trova, Python segue l’Ordine di Risoluzione dei Metodi per cercarlo nelle classi di base. E l’Ordine di Risoluzione dei Metodi sarà da sinistra a destra nel caso dell’ereditarietà multipla e dal basso verso l’alto nel caso dell’ereditarietà a più livelli. Ciò consente agli sviluppatori di utilizzare l’ereditarietà multipla in modo efficace senza ambiguità.

Ora, se controlliamo l’output, si farà riferimento a speak nella classe Bird in quanto viene chiamato per primo.

Anfibio().parlare()# Output - L'uccello cinguetta

sebbene l’Ordine di Risoluzione dei Metodi assicuri che non ottenghiamo errori, i Mixin possono anche essere utilizzati per evitare l’ereditarietà multipla e fornire funzionalità aggiuntive.

Mixin

I Mixin sono classi progettate per essere piccole e focalizzate su una singola funzionalità. Forniscono un modo per condividere funzionalità tra più classi senza utilizzare l’ereditarietà tradizionale. Aiutano a prevenire il problema Diamond e a evitare gerarchie complesse.

class JSONMixin:
    def to_json(self):
        import json
        return json.dumps(self.__dict__)

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

class JSONPerson(Person, JSONMixin):
    pass

person = JSONPerson("Alice", 30)
print(person.to_json())

Altri Problemi dell’Ereditarietà

  • Le classi ereditate sono strettamente accoppiate alle classi di base e ogni volta che la funzionalità della classe di base viene modificata, bisogna testare tutte le classi ereditate per assicurare un codice privo di errori. Per questo motivo, è necessaria una corretta strutturazione della classe di base e una gestione attenta dell’ereditarietà. Mantenere metodi di supporto nella classe di base e, per quanto possibile, evitare l’ereditarietà o la modifica di codice statico nella classe figlia. Non selezionare una classe di base fragile e non manutenibile.
  • Il sovrascritto errato dei metodi o l’utilizzo errato dei metodi super() possono produrre un codice prone agli errori. Non mantenere gerarchie complesse poiché Python non forza l’incapsulamento e le firme dei metodi, possono esserci problemi durante l’esecuzione se non viene eseguito il sovrascritto corretto.
  • Ereditarietà vs. Composizione: L’ereditarietà non è sempre la soluzione migliore, in generale la composizione crea un oggetto complesso combinando oggetti semplici anziché ereditare il comportamento dalle classi figlie. Scegliere la composizione anziché l’ereditarietà quando possibile, in quanto favorisce anche la riutilizzabilità del codice e la modularità e evita problemi nell’ereditarietà come il problema del Diamante, l’accoppiamento stretto e le inflessibilità.

Ecco tutto quello che ho da dire sui comuni problemi dell’ereditarietà. Spero che questo sia utile… Buon codice…

Riferimenti: https://www.python.org/download/releases/2.3/mro/