2009-04-15 8 views
100

stavo leggendo 'Dive Into Python' e nel capitolo sulle classi che dà questo esempio:Eredità e __init__ imperativi di pitone

class FileInfo(UserDict): 
    "store file metadata" 
    def __init__(self, filename=None): 
     UserDict.__init__(self) 
     self["name"] = filename 

L'autore dice poi che se si desidera eseguire l'override del metodo __init__, è deve chiamare esplicitamente il genitore __init__ con i parametri corretti.

  1. E se quella classe FileInfo avesse più di una classe di antenati?
    • Devo chiamare esplicitamente tutti i metodi __init__ delle classi antenate?
  2. Inoltre, devo eseguire questa operazione con qualsiasi altro metodo che voglio sovrascrivere?
+3

Si noti che il sovraccarico è un concetto separato da Overriding. –

+2

sì, grazie per averlo risolto –

risposta

129

Il libro è un po 'datato rispetto alla chiamata della sottoclasse-superclasse. È anche un po 'datato rispetto alle classi built-in sottoclasse.

Al giorno d'oggi sembra così.

class FileInfo(dict): 
    """store file metadata""" 
    def __init__(self, filename=None): 
     super(FileInfo, self).__init__() 
     self["name"] = filename 

Tenere presente quanto segue.

  1. Siamo in grado di creare una sottoclasse direttamente classi incorporate, come dict, list, tuple, ecc

  2. La funzione super maniglie rintracciare superclassi di questa classe e le funzioni di chiamata in modo appropriato.

+4

dovrei cercare un libro/tutorial migliore? –

+1

Quindi, nel caso di ereditarietà multipla, super() li tiene traccia per conto tuo? – Dana

+2

Cosa c'è di sbagliato in dict .__ init __(), esattamente? –

3

Se la classe FileInfo ha più di una classe di antenato, è necessario chiamare tutte le loro funzioni __init __(). Dovresti fare lo stesso anche per la funzione __del __(), che è un distruttore.

2

Sì, è necessario chiamare __init__ per ogni classe genitore. Lo stesso vale per le funzioni, se si sostituisce una funzione esistente in entrambi i genitori.

10

non si ha realmente hanno per chiamare i __init__ metodi della classe di base (es), ma di solito si vuole di farlo perché le classi di base faranno alcune inizializzazioni importanti là che sono necessari per il riposo di i metodi delle classi per funzionare.

Per altri metodi dipende dalle vostre intenzioni. Se vuoi semplicemente aggiungere qualcosa al comportamento delle classi base, vorrai chiamare il metodo delle classi base in aggiunta al tuo codice personale. Se si desidera modificare radicalmente il comportamento, è possibile che non si chiami il metodo della classe base e che tutte le funzionalità vengano implementate direttamente nella classe derivata.

+4

Per completezza tecnica, alcune classi, come il threading.Thread, generano errori giganteschi se si tenta di evitare di chiamare __init__ del genitore. –

+5

Trovo che tutto questo "non devi chiamare il costruttore della base" parla estremamente irritante. Non devi chiamarlo in nessuna lingua che conosco. Tutti loro rovineranno (o meno) più o meno allo stesso modo, non inizializzando i membri. Suggerire di non inizializzare le classi base è semplicemente sbagliato in così tanti modi. Se una classe non ha bisogno di inizializzazione ora ne avrà bisogno in futuro. Il costruttore è parte dell'interfaccia della struttura di classe/linguaggio costrutto e dovrebbe essere usato correttamente. L'uso corretto è di chiamarlo a volte nel costruttore del tuo derivato. Allora fallo. – AndreasT

17

In ogni classe da cui è necessario ereditare, è possibile eseguire un ciclo di ogni classe che deve essere inizializzata all'avvio della classe figlio ... un esempio che può essere copiato potrebbe essere meglio compreso ...

class Female_Grandparent: 
    def __init__(self): 
     self.grandma_name = 'Grandma' 

class Male_Grandparent: 
    def __init__(self): 
     self.grandpa_name = 'Grandpa' 

class Parent(Female_Grandparent, Male_Grandparent): 
    def __init__(self): 
     Female_Grandparent.__init__(self) 
     Male_Grandparent.__init__(self) 

     self.parent_name = 'Parent Class' 

class Child(Parent): 
    def __init__(self): 
     Parent.__init__(self) 
#---------------------------------------------------------------------------------------# 
     for cls in Parent.__bases__: # This block grabs the classes of the child 
      cls.__init__(self)  # class (which is named 'Parent' in this case), 
            # and iterates through them, initiating each one. 
            # The result is that each parent, of each child, 
            # is automatically handled upon initiation of the 
            # dependent class. WOOT WOOT! :D 
#---------------------------------------------------------------------------------------# 



g = Female_Grandparent() 
print g.grandma_name 

p = Parent() 
print p.grandma_name 

child = Child() 

print child.grandma_name 
+1

Non sembra che il ciclo for in 'Child .__ init__' sia necessario. Quando lo rimuovo dall'esempio, il bambino stampa ancora "Nonna". Il nonno init è gestito dalla classe 'Parent'? – Adam

+2

Penso che i granparents init siano già gestiti da init di Parent, vero? – johk95