Chiamare il metodo di una classe genitore dalla classe figlio?


608

Quando creo una semplice gerarchia di oggetti in Python, vorrei essere in grado di invocare metodi della classe genitore da una classe derivata. In Perl e Java, esiste una parola chiave per this ( super). In Perl, potrei fare questo:

package Foo;

sub frotz {
    return "Bamf";
}

package Bar;
@ISA = qw(Foo);

sub frotz {
   my $str = SUPER::frotz();
   return uc($str);
}

In Python, sembra che devo nominare esplicitamente la classe genitore dal bambino. Nell'esempio sopra, dovrei fare qualcosa del genere Foo::frotz().

Questo non sembra giusto poiché questo comportamento rende difficile creare gerarchie profonde. Se i bambini hanno bisogno di sapere quale classe ha definito un metodo ereditato, allora viene creato ogni sorta di dolore informativo.

È una vera limitazione in Python, una lacuna nella mia comprensione o entrambi?


3
Penso che la denominazione della classe genitore non sia una cattiva idea. Può essere utile quando la classe figlio eredita da più di un genitore, poiché si nomina esplicitamente la classe genitore.

7
Mentre un'opzione per nominare una classe non è una cattiva idea, essere costretti a farlo sicuramente lo è.
johndodo,

Fai attenzione ai cambiamenti nella super gestione tra Python 2 e Python 3 https://www.python.org/dev/peps/pep-3135/ . Non è più necessario nominare la classe (sebbene possa essere ancora un'idea divina, almeno a volte).
jwpfox,

Risposte:


737

Sì, ma solo con lezioni di nuovo stile . Usa la super()funzione:

class Foo(Bar):
    def baz(self, arg):
        return super().baz(arg)

Per python <3, utilizzare:

class Foo(Bar):
    def baz(self, arg):
        return super(Foo, self).baz(arg)

53
Non puoi anche fare "return Bar.baz (self, arg)"? In tal caso, quale sarebbe meglio?

15
Sì, puoi, ma l'OP stava chiedendo come farlo senza nominare esplicitamente la superclasse sul sito della chiamata (Bar, in questo caso).
Adam Rosenfield,

7
super () è strano con ereditarietà multipla. Chiama alla classe "successiva". Potrebbe essere la classe genitore o potrebbe essere una classe completamente non correlata che appare altrove nella gerarchia.
Steve Jessop,

6
Uso Python 2.x e ottengo "Errore: deve essere di tipo, non classobj" quando lo faccio
Chris F

28
La sintassi super(Foo, self)è corretta per Python 2.x? In Python 3.x, super()si preferisce il corretto?
Trevor Boyd Smith,

144

Python ha anche super :

super(type[, object-or-type])

Restituisce un oggetto proxy che delega le chiamate del metodo a una classe di tipo genitore o fratello. Ciò è utile per accedere a metodi ereditati che sono stati sovrascritti in una classe. L'ordine di ricerca è uguale a quello utilizzato da getattr () tranne per il fatto che il tipo stesso viene ignorato.

Esempio:

class A(object):     # deriving from 'object' declares A as a 'new-style-class'
    def foo(self):
        print "foo"

class B(A):
    def foo(self):
        super(B, self).foo()   # calls 'A.foo()'

myB = B()
myB.foo()

7
Esempio migliore di quello di Adam perché hai dimostrato di usare una classe di nuovo stile con la classe base dell'oggetto. Manca solo il collegamento a classi di nuovo stile.
Samuel,

83
ImmediateParentClass.frotz(self)

andrà bene, indipendentemente dal fatto che la classe genitrice immediata si sia definita frotzo ereditata. superè necessario solo per il corretto supporto dell'ereditarietà multipla (e quindi funziona solo se ogni classe lo utilizza correttamente). In generale, AnyClass.whateversta andando a guardare in alto whatevera AnyClass's antenati se AnyClassnon definisce / ignorare, e questo vale per 'classe figlio chiamando il metodo del genitore' come per qualsiasi altro evento!


Ciò impone che anche ImmediateParentClass sia presente nell'ambito per l'esecuzione di questa istruzione. Il solo fatto di importare l'oggetto da un modulo non sarà di aiuto.
Tushar Vazirani,

e se fosse un metodo di classe?
Shiplu Mokaddim,

@TusharVazirani se la classe genitore è una classe immediata parent, rientra nell'ambito di tutta la classe, no?
Yaroslav Nikitenko

1
@YaroslavNikitenko Sto parlando dell'importazione di un'istanza, non della classe.
Tushar Vazirani,

1
@TusharVazirani ora ho capito, grazie. Stavo pensando di chiamare dal metodo della classe corrente (come affermato dall'OP).
Yaroslav Nikitenko

75

Python 3 ha una sintassi diversa e più semplice per chiamare il metodo parent.

Se la Fooclasse eredita da Bar, allora da Bar.__init__può essere invocato da Foovia super().__init__():

class Foo(Bar):

    def __init__(self, *args, **kwargs):
        # invoke Bar.__init__
        super().__init__(*args, **kwargs)

5
Bello sentire Python 3 ha una soluzione migliore. La domanda viene da Python 2.X. Grazie per il suggerimento però.
Giovanni,

46

Molte risposte hanno spiegato come chiamare un metodo dal genitore che è stato ignorato nel bambino.

però

"come si chiama il metodo di una classe genitore dalla classe figlio?"

potrebbe anche significare solo:

"come si chiamano i metodi ereditati?"

È possibile chiamare metodi ereditati da una classe genitore proprio come se fossero metodi della classe figlio, purché non siano stati sovrascritti.

ad esempio in Python 3:

class A():
  def bar(self, string):
    print("Hi, I'm bar, inherited from A"+string)

class B(A):
  def baz(self):
    self.bar(" - called by baz in B")

B().baz() # prints out "Hi, I'm bar, inherited from A - called by baz in B"

sì, questo può essere abbastanza ovvio, ma ritengo che senza sottolineare ciò le persone potrebbero lasciare questo thread con l'impressione che si debba saltare attraverso cerchi ridicoli solo per accedere ai metodi ereditati in Python. Soprattutto perché questa domanda ha un alto valore nelle ricerche di "come accedere al metodo di una classe genitore in Python" e l'OP è scritto dal punto di vista di qualcuno che non conosce Python.

Ho trovato: https://docs.python.org/3/tutorial/classes.html#inheritance per essere utile per comprendere come si accede ai metodi ereditati.


@gizzmole in quale situazione non funziona? Sono felice di aggiungere avvertimenti o avvertenze pertinenti, ma non sono riuscito a capire come il link che hai pubblicato fosse pertinente a questa risposta.
Ben

@Ben Mi dispiace, non ho letto abbastanza attentamente la tua risposta. Questa risposta non risponde alla domanda su come sovraccaricare le funzioni, ma risponde a una domanda diversa. Ho cancellato il mio commento iniziale.
gizzmole

@gizzmole ah, non preoccuparti. Mi rendo conto che questa risposta non si applica alle funzioni di sovraccarico, tuttavia la domanda non menziona esplicitamente il sovraccarico (anche se l'esempio perl fornito mostra un caso di sovraccarico). La stragrande maggioranza delle altre risposte copre il sovraccarico - questo è qui per quelle persone che non ne hanno bisogno.
Ben

@Ben cosa succede se voglio chiamare B (). Baz () da un'altra classe denominata 'C' usando le variabili definite in C ?. Ho provato a fare B (). Baz () in classe C, dove self.string è un'altra val. Non mi ha dato nessuno, quando l'ho giustiziato
joey,

@joey Non sono sicuro di cosa stai cercando di fare o se rientra nell'ambito di questa domanda. String è una variabile locale - l'impostazione self.stringnon farà nulla. In classe C potresti comunque chiamareB().bar(string)
Ben

27

Ecco un esempio dell'uso di super () :

#New-style classes inherit from object, or from another new-style class
class Dog(object):

    name = ''
    moves = []

    def __init__(self, name):
        self.name = name

    def moves_setup(self):
        self.moves.append('walk')
        self.moves.append('run')

    def get_moves(self):
        return self.moves

class Superdog(Dog):

    #Let's try to append new fly ability to our Superdog
    def moves_setup(self):
        #Set default moves by calling method of parent class
        super(Superdog, self).moves_setup()
        self.moves.append('fly')

dog = Superdog('Freddy')
print dog.name # Freddy
dog.moves_setup()
print dog.get_moves() # ['walk', 'run', 'fly']. 
#As you can see our Superdog has all moves defined in the base Dog class

Non è un cattivo esempio? Credo che non sia davvero sicuro usare Super a meno che la tua intera gerarchia non sia classe "nuovo stile" e che chiami correttamente Super ().
Init

14

C'è un super () anche in Python. È un po 'traballante, a causa delle classi di vecchio e nuovo stile di Python, ma è abbastanza comunemente usato ad esempio nei costruttori:

class Foo(Bar):
    def __init__(self):
        super(Foo, self).__init__()
        self.baz = 5

10

Consiglierei di usare CLASS.__bases__ qualcosa del genere

class A:
   def __init__(self):
        print "I am Class %s"%self.__class__.__name__
        for parentClass in self.__class__.__bases__:
              print "   I am inherited from:",parentClass.__name__
              #parentClass.foo(self) <- call parents function with self as first param
class B(A):pass
class C(B):pass
a,b,c = A(),B(),C()

1
Tieni presente che, come puoi vedere dal volume di riferimenti a super qui, che la gente detesta davvero usare le basi a meno che tu non stia facendo qualcosa di veramente profondo. Se non ti piace il super , guarda mro . Prendere la prima voce in mro è più sicuro del tracciare quale voce nelle basi usare. L'ereditarietà multipla in Python esegue la scansione di alcune cose all'indietro e altre in avanti, quindi lasciare che la lingua svolga il processo è più sicuro.
Jon Jay Obermark,


5

C'è un super () anche in Python.

Esempio di come viene chiamato un metodo di superclasse da un metodo di sottoclasse

class Dog(object):
    name = ''
    moves = []

    def __init__(self, name):
        self.name = name

    def moves_setup(self,x):
        self.moves.append('walk')
        self.moves.append('run')
        self.moves.append(x)
    def get_moves(self):
        return self.moves

class Superdog(Dog):

    #Let's try to append new fly ability to our Superdog
    def moves_setup(self):
        #Set default moves by calling method of parent class
        super().moves_setup("hello world")
        self.moves.append('fly')
dog = Superdog('Freddy')
print (dog.name)
dog.moves_setup()
print (dog.get_moves()) 

Questo esempio è simile a quello spiegato sopra.Tuttavia c'è una differenza che super non ha alcun argomento passato ad esso. Questo codice sopra è eseguibile nella versione 3.4 di Python.


3

In questo esempio cafec_paramè una classe base (classe genitore) ed abcè una classe figlio. abcchiama il AWCmetodo nella classe base.

class cafec_param:

    def __init__(self,precip,pe,awc,nmonths):

        self.precip = precip
        self.pe = pe
        self.awc = awc
        self.nmonths = nmonths

    def AWC(self):

        if self.awc<254:
            Ss = self.awc
            Su = 0
            self.Ss=Ss
        else:
            Ss = 254; Su = self.awc-254
            self.Ss=Ss + Su   
        AWC = Ss + Su
        return self.Ss


    def test(self):
        return self.Ss
        #return self.Ss*4

class abc(cafec_param):
    def rr(self):
        return self.AWC()


ee=cafec_param('re',34,56,2)
dd=abc('re',34,56,2)
print(dd.rr())
print(ee.AWC())
print(ee.test())

Produzione

56

56

56

2

In Python 2, non ho avuto molta fortuna con super (). Ho usato la risposta di jimifiki su questo thread SO come fare riferimento a un metodo genitore in Python? . Quindi, ho aggiunto la mia piccola svolta ad esso, che penso sia un miglioramento dell'usabilità (specialmente se hai nomi di classe lunghi).

Definire la classe di base in un modulo:

 # myA.py

class A():     
    def foo( self ):
        print "foo"

Quindi importare la classe in altri moduli as parent:

# myB.py

from myA import A as parent

class B( parent ):
    def foo( self ):
        parent.foo( self )   # calls 'A.foo()'

2
Devi usare class A(object): invece di class A(): Then super () funzionerà
blndev il

Non sono sicuro di seguirlo. Intendi nella definizione della classe? Ovviamente, devi indicare che una classe ha un genitore in qualche modo per pensare che potresti fare riferimento a un genitore! Da quando l'ho pubblicato 9 mesi fa, sono un po 'sfocato nei dettagli, ma penso di aver inteso che gli esempi di Arun Ghosh, Lawrence, Kkk, ecc. Non sono applicabili in Py 2.x. Invece, ecco un metodo che funziona e un modo semplice per fare riferimento al genitore, quindi è un po 'più chiaro cosa stai facendo. (come usare super)
BuvinJ

0
class department:
    campus_name="attock"
    def printer(self):
        print(self.campus_name)

class CS_dept(department):
    def overr_CS(self):
        department.printer(self)
        print("i am child class1")

c=CS_dept()
c.overr_CS()

-3
class a(object):
    def my_hello(self):
        print "hello ravi"

class b(a):
    def my_hello(self):
    super(b,self).my_hello()
    print "hi"

obj = b()
obj.my_hello()

2
Benvenuto in StackOverflow. A questa domanda è già stata data risposta in dettaglio quasi dieci anni fa, la risposta è stata accettata e ampiamente votata e la tua risposta non aggiunge nulla di nuovo. Potresti voler provare a rispondere a domande più recenti (e preferibilmente senza risposta). Come nota a margine, c'è un pulsante nell'editor per applicare la corretta formattazione del codice.
bruno desthuilliers,

-24

Questo è un metodo più astratto:

super(self.__class__,self).baz(arg)

15
self .__ class__ non funziona bene con super poiché self è sempre l'istanza e la sua classe è sempre la classe dell'istanza. Questo significa che se usi super (self .__ class__ .. in una classe e quella classe è ereditata, non funzionerà più.
xitrium

16
Solo per sottolineare, NON USARE QUESTO CODICE. Sconfigge l'intero scopo di super (), ovvero attraversare correttamente l'MRO.
Eevee,

1
stackoverflow.com/a/19257335/419348 ha detto perché non chiamare super(self.__class__, self).baz(arg).
AechoLiu,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.