Appelez la méthode d'une classe parent à partir de la classe enfant?


608

Lors de la création d'une hiérarchie d'objets simple en Python, j'aimerais pouvoir invoquer des méthodes de la classe parente à partir d'une classe dérivée. En Perl et Java, il existe un mot-clé pour cela ( super). En Perl, je pourrais faire ceci:

package Foo;

sub frotz {
    return "Bamf";
}

package Bar;
@ISA = qw(Foo);

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

En Python, il semble que je doive nommer explicitement la classe parente de l'enfant. Dans l'exemple ci-dessus, je devrais faire quelque chose comme Foo::frotz().

Cela ne semble pas correct car ce comportement rend difficile la création de hiérarchies profondes. Si les enfants ont besoin de savoir quelle classe a défini une méthode héritée, alors toutes sortes d'informations sont créées.

Est-ce une limitation réelle en python, une lacune dans ma compréhension ou les deux?


3
Je pense que le nom de la classe parent n'est pas une si mauvaise idée. Cela peut être utile lorsque la classe enfant hérite de plusieurs parents, car vous nommez explicitement la classe parent.

7
Bien qu'une option pour nommer une classe ne soit pas une mauvaise idée, être forcé de le faire l'est certainement.
johndodo

Soyez conscient des changements dans la super gestion entre Python 2 et Python 3 https://www.python.org/dev/peps/pep-3135/ . Nommer la classe n'est plus nécessaire (bien que cela puisse encore être une idée divine, au moins parfois).
jwpfox

Réponses:


737

Oui, mais uniquement avec des classes de nouveau style . Utilisez la super()fonction:

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

Pour python <3, utilisez:

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

53
Ne pouvez-vous pas aussi faire "retourner Bar.baz (self, arg)"? Si oui, lequel serait mieux?

15
Oui, vous pouvez, mais l'OP demandait comment le faire sans nommer explicitement la superclasse sur le site d'appel (Bar, dans ce cas).
Adam Rosenfield

7
super () est bizarre avec un héritage multiple. Il appelle à la "prochaine" classe. Il peut s'agir de la classe parente ou d'une classe complètement indépendante qui apparaît ailleurs dans la hiérarchie.
Steve Jessop

6
J'utilise Python 2.x et j'obtiens "Erreur: doit être de type, pas classobj" quand je fais ça
Chris F

28
La syntaxe super(Foo, self)est-elle correcte pour Python 2.x? Dans Python 3.x, la super()préférence est-elle correcte?
Trevor Boyd Smith

144

Python a également des super :

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

Renvoie un objet proxy qui délègue les appels de méthode à une classe parent ou frère de type. Ceci est utile pour accéder aux méthodes héritées qui ont été remplacées dans une classe. L'ordre de recherche est le même que celui utilisé par getattr () sauf que le type lui-même est ignoré.

Exemple:

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
Meilleur exemple que celui d'Adam car vous avez démontré l'utilisation d'une classe de nouveau style avec la classe de base d'objet. Manque juste le lien vers des classes de nouveau style.
Samuel

83
ImmediateParentClass.frotz(self)

sera très bien, que la classe parent immédiate se soit définie frotzou en ait hérité. supern'est nécessaire que pour la prise en charge appropriée de l' héritage multiple (et cela ne fonctionne que si chaque classe l'utilise correctement). En général, AnyClass.whateverva chercher whateverdans AnyClassles ancêtres de s'il AnyClassne le définit pas / ne le remplace pas, et cela vaut pour la "classe enfant appelant la méthode du parent" comme pour toute autre occurrence!


Cela exige que ImmediateParentClass soit également présent dans la portée pour que cette instruction soit exécutée. Le simple fait d'importer l'objet à partir d'un module n'aidera pas.
Tushar Vazirani

Et si c'est une méthode de classe?
Shiplu Mokaddim

@TusharVazirani si la classe parente est une classe immédiate parent, elle fait partie de la classe entière, n'est-ce pas?
Yaroslav Nikitenko

1
@YaroslavNikitenko Je parle d'importer une instance, pas la classe.
Tushar Vazirani

1
@TusharVazirani maintenant je comprends, merci. Je pensais à appeler à partir de la méthode de la classe actuelle (comme l'a déclaré l'OP).
Yaroslav Nikitenko

75

Python 3 a une syntaxe différente et plus simple pour appeler la méthode parent.

Si la Fooclasse hérite de Bar, alors from Bar.__init__peut être invoqué depuis Foovia super().__init__():

class Foo(Bar):

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

5
Bon d'entendre que python 3 a une meilleure solution. La question vient de python 2.X. Merci pour la suggestion.
jjohn

46

De nombreuses réponses ont expliqué comment appeler une méthode du parent qui a été remplacée par l'enfant.

toutefois

"Comment appelez-vous la méthode d'une classe parent à partir d'une classe enfant?"

pourrait aussi signifier simplement:

"comment appelez-vous les méthodes héritées?"

Vous pouvez appeler des méthodes héritées d'une classe parent comme si elles étaient des méthodes de la classe enfant, tant qu'elles n'ont pas été écrasées.

par exemple en 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"

oui, cela peut être assez évident, mais je pense que sans le souligner, les gens peuvent laisser ce fil avec l'impression que vous devez sauter à travers des cerceaux ridicules juste pour accéder aux méthodes héritées en python. D'autant plus que cette question est très appréciée dans les recherches de "comment accéder à la méthode d'une classe parente en Python", et que l'OP est écrit du point de vue de quelqu'un de nouveau en python.

J'ai trouvé: https://docs.python.org/3/tutorial/classes.html#inheritance utile pour comprendre comment vous accédez aux méthodes héritées.


@gizzmole dans quelle situation cela ne fonctionne pas? Je suis heureux d'ajouter des mises en garde ou des avertissements qui sont pertinents, mais je n'ai pas pu comprendre en quoi le lien que vous avez publié était pertinent pour cette réponse.
Ben

@Ben Désolé, je n'ai pas lu votre réponse suffisamment attentivement. Cette réponse ne répond pas à la question sur la façon de surcharger les fonctions, mais répond à une autre question. J'ai supprimé mon commentaire initial.
gizzmole

@gizzmole ah, pas de soucis. Je me rends compte que cette réponse ne s'applique pas aux fonctions de surcharge, mais la question ne mentionne pas explicitement la surcharge (bien que l'exemple perl donné montre un cas de surcharge). La grande majorité des autres réponses couvrent la surcharge - celle-ci est là pour les personnes qui n'en ont pas besoin.
Ben

@Ben que faire si je veux appeler B (). Baz () depuis une autre classe nommée 'C' en utilisant des variables définies en C ?. J'ai essayé de faire B (). Baz () en classe C, où self.string est un autre val. Ça ne m'a rien donné, quand j'ai exécuté
joey

@joey Je ne sais pas trop ce que vous essayez de faire, ou si c'est dans le cadre de cette question. La chaîne est une variable locale - le réglage self.stringne fera rien. En classe C, vous pouvez cependant appelerB().bar(string)
Ben

27

Voici un exemple d'utilisation de 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

N'est-ce pas un mauvais exemple? Je crois qu'il n'est pas vraiment sûr d'utiliser Super à moins que toute votre hiérarchie ne soit des classes de "nouveau style" et appelle correctement Super ().
Init

14

Il y a aussi un super () en Python. C'est un peu loufoque, à cause des classes de style ancien et nouveau de Python, mais il est assez couramment utilisé par exemple dans les constructeurs:

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

10

Je recommanderais d'utiliser CLASS.__bases__ quelque chose comme ça

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
Gardez à l'esprit que, comme vous pouvez le voir dans le volume de références à super ici, les gens répugnent vraiment à utiliser des bases à moins que vous ne fassiez quelque chose de vraiment profond. Si vous n'aimez pas super , regardez mro . Prendre la première entrée dans mro est plus sûr que de suivre l'entrée dans les bases à utiliser. L'héritage multiple en Python analyse certaines choses en arrière et d'autres en avant, donc laisser le langage dérouler le processus est le plus sûr.
Jon Jay Obermark


5

Il y a aussi un super () en python.

Exemple sur la façon dont une méthode de super classe est appelée à partir d'une méthode de sous-classe

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()) 

Cet exemple est similaire à celui expliqué ci-dessus.Cependant, il y a une différence que super n'a pas d'arguments passés.Ce code ci-dessus est exécutable dans la version python 3.4.


3

Dans cet exemple, il cafec_params'agit d'une classe de base (classe parent) et d' abcune classe enfant. abcappelle la AWCméthode dans la classe de 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())

Production

56

56

56

2

En Python 2, je n'ai pas eu beaucoup de chance avec super (). J'ai utilisé la réponse de jimifiki sur ce fil SO comment faire référence à une méthode parent en python?. Ensuite, j'ai ajouté ma propre petite touche, ce qui, je pense, est une amélioration de la convivialité (surtout si vous avez des noms de classe longs).

Définissez la classe de base dans un module:

 # myA.py

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

Importez ensuite la classe dans un autre module as parent:

# myB.py

from myA import A as parent

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

2
Vous devez utiliser à la class A(object): place de class A(): Then super () fonctionnera
blndev

Je ne suis pas sûr de suivre. Voulez-vous dire dans la définition de la classe? Évidemment, vous devez indiquer qu'une classe a un parent d'une manière ou d'une autre afin de penser que vous pourriez vous référer à un parent! Depuis, j'ai posté cela il y a 9 mois, je suis un peu flou dans les détails, mais je pense que je voulais dire que les exemples d'Arun Ghosh, lawrence, kkk, etc. ne sont pas applicables dans Py 2.x. Au lieu de cela, voici une méthode qui fonctionne et un moyen facile de se référer au parent afin qu'il soit un peu plus clair ce que vous faites. (comme utiliser 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
Bienvenue sur stackoverflow. Cette question a déjà reçu une réponse détaillée il y a près de dix ans maintenant, la réponse a été acceptée et largement votée, et votre réponse n'ajoute rien de nouveau. Vous voudrez peut-être essayer de répondre à des questions plus récentes (et de préférence sans réponse) à la place. En remarque, il y a un bouton dans l'éditeur pour appliquer une mise en forme de code appropriée.
bruno desthuilliers

-24

Il s'agit d'une méthode plus abstraite:

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

15
self .__ class__ ne fonctionne pas correctement avec super car self est toujours l'instance et sa classe est toujours la classe de l'instance. Cela signifie que si vous utilisez super (self .__ class__ .. dans une classe et que cette classe est héritée, cela ne fonctionnera plus.
xitrium

16
Juste pour souligner, N'UTILISEZ PAS CE CODE. Il bat tout le but de super (), qui est de traverser correctement le MRO.
Evoli

1
stackoverflow.com/a/19257335/419348 a dit pourquoi ne pas appeler super(self.__class__, self).baz(arg).
AechoLiu
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.