Eredità e polimorfismo

Ereditarietà e polimorfismo: questo è un concetto molto importante in Python. Devi capirlo meglio se vuoi imparare.

Eredità

Uno dei principali vantaggi della programmazione orientata agli oggetti è il riutilizzo. L'ereditarietà è uno dei meccanismi per ottenere lo stesso risultato. L'ereditarietà consente al programmatore di creare prima una classe generale o di base e successivamente estenderla a una classe più specializzata. Consente al programmatore di scrivere codice migliore.

Usando l'ereditarietà puoi usare o ereditare tutti i campi dati e i metodi disponibili nella tua classe base. Successivamente è possibile aggiungere metodi e campi dati personali, quindi l'ereditarietà fornisce un modo per organizzare il codice, anziché riscriverlo da zero.

Nella terminologia orientata agli oggetti, quando la classe X estende la classe Y, Y è chiamata classe super / genitore / base e X è chiamata sottoclasse / figlio / classe derivata. Un punto da notare qui è che solo i campi dati e il metodo che non sono privati ​​sono accessibili dalle classi figlie. I campi e i metodi di dati privati ​​sono accessibili solo all'interno della classe.

la sintassi per creare una classe derivata è:

class BaseClass:
   Body of base class
class DerivedClass(BaseClass):
   Body of derived class

Attributi ereditari

Ora guarda l'esempio seguente:

Produzione

Per prima cosa abbiamo creato una classe chiamata Date e passiamo l'oggetto come argomento, here-object è una classe incorporata fornita da Python. Successivamente abbiamo creato un'altra classe chiamata time e chiamata la classe Date come argomento. Tramite questa chiamata abbiamo accesso a tutti i dati e gli attributi della classe Date nella classe Time. Per questo motivo quando proviamo a ottenere il metodo get_date dall'oggetto della classe Time tm che abbiamo creato prima possibile.

Object.Attribute Lookup Hierarchy

  • L'istanza
  • La classe
  • Qualsiasi classe da cui eredita questa classe

Esempi di ereditarietà

Diamo uno sguardo conclusivo all'esempio dell'ereditarietà:

Creiamo un paio di classi per partecipare agli esempi:

  • Animale: la classe simula un animale
  • Gatto - Sottoclasse di animali
  • Cane - Sottoclasse di animali

In Python, il costruttore della classe utilizzato per creare un oggetto (istanza) e assegnare il valore per gli attributi.

Costruttore di sottoclassi sempre chiamato a un costruttore della classe genitore per inizializzare il valore per gli attributi nella classe genitore, quindi inizia ad assegnare il valore per i suoi attributi.

Produzione

Nell'esempio precedente, vediamo gli attributi oi metodi del comando che abbiamo inserito nella classe genitore in modo che tutte le sottoclassi o classi figlie erediteranno quella proprietà dalla classe genitore.

Se una sottoclasse tenta di ereditare metodi o dati da un'altra sottoclasse, lo farà tramite un errore, come vediamo quando la classe Dog tenta di chiamare i metodi swatstring () da quella classe cat, genera un errore (come AttributeError nel nostro caso).

Polimorfismo ("MOLTE FORME")

Il polimorfismo è una caratteristica importante della definizione di classi in Python che viene utilizzata quando si hanno nomi comuni di metodi tra classi o sottoclassi. Ciò consente alle funzioni di utilizzare entità di tipi diversi in momenti diversi. Pertanto, fornisce flessibilità e accoppiamento libero in modo che il codice possa essere esteso e mantenuto facilmente nel tempo.

Ciò consente alle funzioni di utilizzare oggetti di una qualsiasi di queste classi polimorfiche senza dover essere consapevoli delle distinzioni tra le classi.

Il polimorfismo può essere eseguito attraverso l'ereditarietà, con sottoclassi che fanno uso di metodi della classe base o li sovrascrivono.

Comprendiamo il concetto di polimorfismo con il nostro precedente esempio di ereditarietà e aggiungiamo un metodo comune chiamato show_affection in entrambe le sottoclassi -

Dall'esempio che possiamo vedere, si riferisce a un progetto in cui un oggetto di tipo dissimile può essere trattato nello stesso modo o più specificamente due o più classi con metodo con lo stesso nome o interfaccia comune perché stesso metodo (show_affection nell'esempio sotto) viene chiamato con entrambi i tipi di oggetti.

Produzione

Quindi, tutti gli animali mostrano affetti (show_affection), ma lo fanno in modo diverso. I comportamenti “show_affection” sono quindi polimorfici nel senso che agivano in modo diverso a seconda dell'animale. Quindi, il concetto astratto di "animale" non significa in realtà "mostra_affetto", ma animali specifici (come cani e gatti) hanno una concreta attuazione dell'azione "mostra_affetto".

Lo stesso Python ha classi che sono polimorfiche. Ad esempio, la funzione len () può essere utilizzata con più oggetti e tutti restituiscono l'output corretto in base al parametro di input.

Overriding

In Python, quando una sottoclasse contiene un metodo che sovrascrive un metodo della superclasse, puoi anche chiamare il metodo della superclasse chiamando

Super (sottoclasse, self) .method invece di self.method.

Esempio

class Thought(object):
   def __init__(self):
      pass
   def message(self):
      print("Thought, always come and go")

class Advice(Thought):
   def __init__(self):
      super(Advice, self).__init__()
   def message(self):
      print('Warning: Risk is always involved when you are dealing with market!')

Ereditare il costruttore

Se vediamo dal nostro precedente esempio di ereditarietà, __init__ si trovava nella classe genitore in alto perché la classe figlio cane o gatto non aveva il metodo __init__ in esso. Python ha utilizzato la ricerca dell'attributo di ereditarietà per trovare __init__ nella classe animale. Quando abbiamo creato la classe figlio, prima cercherà il metodo __init__ nella classe cane, poi non lo ha trovato, quindi ha esaminato la classe genitore Animal e vi ha trovato e l'ha chiamato lì. Quindi, poiché la progettazione della nostra classe è diventata complessa, potremmo voler inizializzare un'istanza elaborandola prima tramite il costruttore della classe padre e poi tramite il costruttore della classe figlio.

Produzione

Nell'esempio sopra, tutti gli animali hanno un nome e tutti i cani una razza particolare. Abbiamo chiamato il costruttore della classe genitore con super. Quindi il cane ha il suo __init__ ma la prima cosa che accade è che chiamiamo super. Super è una funzione incorporata ed è progettato per correlare una classe alla sua super classe o alla sua classe genitore.

In questo caso diciamo che prendi la super classe dog e passa l'istanza dog a qualunque metodo diciamo qui il costruttore __init__. Quindi, in altre parole, chiamiamo la classe genitore Animal __init__ con l'oggetto dog. Potresti chiederti perché non diciamo semplicemente Animal __init__ con l'istanza del cane, potremmo farlo, ma se il nome della classe animale dovesse cambiare, in futuro. E se volessimo riorganizzare la gerarchia delle classi, in modo che il cane abbia ereditato da un'altra classe. L'utilizzo di super in questo caso ci consente di mantenere le cose modulari e facili da modificare e mantenere.

Quindi in questo esempio siamo in grado di combinare la funzionalità generale __init__ con funzionalità più specifiche. Questo ci dà l'opportunità di separare la funzionalità comune dalla funzionalità specifica che può eliminare la duplicazione del codice e mettere in relazione le classi l'una con l'altra in un modo che riflette il design complessivo del sistema.

Conclusione

  • __init__ è come qualsiasi altro metodo; può essere ereditato

  • Se una classe non ha un costruttore __init__, Python controllerà la sua classe genitore per vedere se riesce a trovarne uno.

  • Non appena ne trova uno, Python lo chiama e smette di cercare

  • Possiamo usare la funzione super () per chiamare i metodi nella classe genitore.

  • Potremmo voler inizializzare nel genitore così come nella nostra classe.

Ereditarietà multipla e albero di ricerca

Come indica il nome, l'ereditarietà multipla è Python quando una classe eredita da più classi.

Ad esempio, un bambino eredita i tratti della personalità da entrambi i genitori (madre e padre).

Sintassi dell'ereditarietà multipla Python

Per fare in modo che una classe erediti da più classi genitori, scriviamo i nomi di queste classi all'interno delle parentesi alla classe derivata mentre la definiamo. Separiamo questi nomi con una virgola.

Di seguito è riportato un esempio di ciò:

>>> class Mother:
   pass

>>> class Father:
   pass

>>> class Child(Mother, Father):
   pass

>>> issubclass(Child, Mother) and issubclass(Child, Father)
True

L'ereditarietà multipla si riferisce alla capacità di ereditare da due o più di due classi. La complessità sorge quando il bambino eredita dal genitore e i genitori ereditano dalla classe dei nonni. Python si arrampica su un albero ereditario alla ricerca di attributi che viene richiesto di essere letti da un oggetto. Controllerà nell'istanza, all'interno della classe, quindi della classe genitrice e infine dalla classe dei nonni. Ora sorge la domanda in quale ordine verranno cercate le classi: prima il respiro o prima la profondità. Per impostazione predefinita, Python va con il depth-first.

Ecco perché nel diagramma sottostante il Python cerca il metodo dothis () prima nella classe A. Quindi l'ordine di risoluzione del metodo nell'esempio sotto sarà

Mro- D→B→A→C

Guarda il seguente diagramma di ereditarietà multipla:

Facciamo un esempio per capire la caratteristica "mro" di un Python.

Produzione

Esempio 3

Facciamo un altro esempio di ereditarietà multipla "a forma di diamante".

Il diagramma sopra sarà considerato ambiguo. Dal nostro precedente esempio di comprensione dell '"ordine di risoluzione del metodo" .ie mro sarà D → B → A → C → A ma non lo è. Ottenendo la seconda A dalla C, Python ignorerà la precedente A. quindi l'mro in questo caso sarà D → B → C → A.

Creiamo un esempio basato sul diagramma sopra -

Produzione

Una regola semplice per comprendere l'output di cui sopra è: se la stessa classe appare nell'ordine di risoluzione del metodo, le prime apparizioni di questa classe verranno rimosse dall'ordine di risoluzione del metodo.

In conclusione -

  • Qualsiasi classe può ereditare da più classi

  • Python normalmente usa un ordine "profondità prima" durante la ricerca di classi che ereditano.

  • Ma quando due classi ereditano dalla stessa classe, Python elimina le prime apparizioni di quella classe dal mro.

Decoratori, metodi statici e di classe

Le funzioni (o metodi) vengono create dall'istruzione def.

Sebbene i metodi funzionino esattamente allo stesso modo di una funzione tranne un punto in cui il primo argomento del metodo è l'oggetto istanza.

Possiamo classificare i metodi in base a come si comportano, come

  • Simple method- definito al di fuori di una classe. Questa funzione può accedere agli attributi della classe fornendo l'argomento dell'istanza:

def outside_func(():
  • Instance method -

def func(self,)
  • Class method - se abbiamo bisogno di usare attributi di classe

@classmethod
def cfunc(cls,)
  • Static method - non hai informazioni sulla classe

@staticmethod
def sfoo()

Fino ad ora abbiamo visto il metodo dell'istanza, ora è il momento di dare un'occhiata agli altri due metodi,

Metodo di classe

Il decoratore @classmethod, è un decoratore di funzione incorporato che viene passato alla classe su cui è stato chiamato o alla classe dell'istanza su cui è stato chiamato come primo argomento. Il risultato di tale valutazione mette in ombra la definizione della funzione.

sintassi

class C(object):
   @classmethod
   def fun(cls, arg1, arg2, ...):
      ....
fun: function that needs to be converted into a class method
returns: a class method for function

Hanno accesso a questo argomento cls, non possono modificare lo stato dell'istanza dell'oggetto. Ciò richiederebbe l'accesso al sé.

  • È legato alla classe e non all'oggetto della classe.

  • I metodi di classe possono comunque modificare lo stato della classe che si applica a tutte le istanze della classe.

Metodo statico

Un metodo statico non accetta né un parametro self né un parametro cls (class) ma è libero di accettare un numero arbitrario di altri parametri.

syntax

class C(object):
   @staticmethod
   def fun(arg1, arg2, ...):
   ...
returns: a static method for function funself.
  • Un metodo statico non può modificare lo stato dell'oggetto né lo stato della classe.
  • Sono limitati a quali dati possono accedere.

Quando usare cosa

  • Generalmente usiamo il metodo di classe per creare metodi di fabbrica. I metodi factory restituiscono un oggetto classe (simile a un costruttore) per diversi casi d'uso.

  • Generalmente utilizziamo metodi statici per creare funzioni di utilità.