Selon les documents sur l'héritage :
Les classes dérivées peuvent remplacer les méthodes de leurs classes de base. Étant donné que les méthodes n'ont aucun privilège spécial lors de l'appel d'autres méthodes du même objet, une méthode d'une classe de base qui appelle une autre méthode définie dans la même classe de base peut finir par appeler une méthode d'une classe dérivée qui la remplace.
Comment cela se produit-il? Quelqu'un peut-il illustrer ce concept avec un exemple simple?
Voici l'exemple que vous avez demandé. Ceci affiche chocolate
.
class Base:
def foo(self):
print("foo")
def bar(self):
self.foo()
class Derived(Base):
def foo(self):
print("chocolate")
d = Derived()
d.bar() # prints "chocolate"
La chaîne chocolate
est imprimée à la place de foo
car Derived
remplace la fonction foo()
. Même si bar()
est défini dans Base
, il finit par appeler l'implémentation Derived
de foo()
au lieu de l'implémentation Base
.
Lorsqu'une recherche d'attribut est effectuée sur une instance de la classe, le dictionnaire de classe et les dictionnaires de ses classes de base sont recherchés dans un certain ordre ( voir: Ordre de résolution des méthodes ) pour la méthode appropriée. Ce qui est trouvé d'abord va être appelé.
En utilisant l'exemple Spam
suivant:
class Spam:
def produce_spam(self):
print("spam")
def get_spam(self):
self.produce_spam()
class SuperSpam(Spam):
def produce_spam(self):
print("super spam")
Spam
définit les fonctions produce_spam
et get_spam
. Ceux-ci vivent dans son Spam.__dict__
(espace de noms de classe). La sous-classe SuperSpam
, par héritage, a accès à ces deux méthodes. SuperSpam.produce_spam
ne remplace pas Spam.produce_spam
, il est simplement trouvé en premier lors de la recherche du nom 'produce_spam'
est créé sur l'une de ses instances.
Essentiellement, le résultat de l'héritage est que les dictionnaires de toutes les classes de base seront également recherchés si, après une recherche d'attribut sur la sous-classe, l'attribut ne se trouve pas dans le dictionnaire de la sous-classe.
Lorsque la fonction get_spam
est d'abord invoqué avec:
s = SuperSpam()
s.get_spam()
la séquence des événements à peu près va comme ceci:
SuperSpam
s __dict__
pour get_spam
.SuperSpam
s __dict__
examinez les dictionnaires de ses classes de base (chaîne mro
).Spam
est le suivant dans la chaîne mro
, donc get_spam
se trouve dans le dictionnaire de Spam
.Maintenant, quand produce_spam
est recherché dans le corps de get_spam
avec self.produce_spam
, la séquence est beaucoup plus courte:
SuperSpam
(self
) __dict__
pour produce_spam
.produce_spam
se trouve dans le __dict__
d'abord pour que cela soit récupéré.
class Base():
def m1(self):
return self.m2()
def m2(self):
return 'base'
class Sub(Base):
def m2(self):
return 'sub'
b = Base()
s = Sub()
print(b.m1(), s.m1())
imprime "base sub"
Pour illustrer son fonctionnement, considérez ces deux classes:
class Parent(object):
def eat(self):
print("I don't want to eat that {}.".format(self.takefrompocket()))
def takefrompocket(self):
return 'Apple'
def __getattribute__(self, name):
print('Looking for:', name)
method_to_use = object.__getattribute__(self, name)
print('Found method:', method_to_use)
return method_to_use
class Child(Parent):
def takefrompocket(self):
return 'salad'
Le __getattribute__
La méthode est responsable dans les nouvelles classes de style (comme toutes les classes en python3) de la recherche d'attribut. Il est simplement implémenté pour print
ce que fait chaque recherche - normalement vous ne voulez pas et ne devez pas l'implémenter vous-même . La recherche suit les pythons ordre de résolution des méthodes (MRO) juste si vous êtes vraiment intéressé.
>>> some_kid = Child()
>>> some_kid.eat()
Looking for: eat
Found method: <bound method Parent.eat of <__main__.Child object at 0x0000027BCA4EEA58>>
Looking for: takefrompocket
Found method: <bound method Child.takefrompocket of <__main__.Child object at 0x0000027BCA4EEA58>>
I don't want to eat that salad.
Ainsi, lorsque vous souhaitez utiliser eat
, il utilise Parent.eat
dans cet exemple. Mais self.takefrompocket
est utilisé à partir de Child
.
>>> some_parent = Parent()
>>> some_parent.eat()
Looking for: eat
Found method: <bound method Parent.eat of <__main__.Parent object at 0x0000027BCA4EE358>>
Looking for: takefrompocket
Found method: <bound method Parent.takefrompocket of <__main__.Parent object at 0x0000027BCA4EE358>>
I don't want to eat that Apple.
Ici, les deux méthodes sont extraites de Parent
. Les classes héritées n'interfèrent pas (généralement) avec leurs ancêtres!