Je vois partout des exemples où les méthodes de la super-classe devraient être appelées par :
super(SuperClass, instance).method(args)
Y a-t-il un inconvénient à faire :
SuperClass.method(instance, args)
Je vois partout des exemples où les méthodes de la super-classe devraient être appelées par :
super(SuperClass, instance).method(args)
Y a-t-il un inconvénient à faire :
SuperClass.method(instance, args)
Considérons la situation suivante :
class A(object):
def __init__(self):
print('Running A.__init__')
super(A,self).__init__()
class B(A):
def __init__(self):
print('Running B.__init__')
# super(B,self).__init__()
A.__init__(self)
class C(A):
def __init__(self):
print('Running C.__init__')
super(C,self).__init__()
class D(B,C):
def __init__(self):
print('Running D.__init__')
super(D,self).__init__()
foo=D()
Les classes forment donc ce qu'on appelle un diamant d'héritage :
A
/ \
B C
\ /
D
En exécutant le code, on obtient
Running D.__init__
Running B.__init__
Running A.__init__
C'est mauvais parce que C
's __init__
est ignoré. La raison en est que B
's __init__
appelle A
's __init__
directement.
Le but de super
est de résoudre les diamants de l'héritage . Si vous dé-commentez
# super(B,self).__init__()
et de commenter
A.__init__(self)
le code donne le résultat le plus souhaitable :
Running D.__init__
Running B.__init__
Running C.__init__
Running A.__init__
Maintenant, tous les __init__
les méthodes sont appelées. Remarquez qu'au moment où vous définissez B.__init__
vous pourriez pensez à que super(B,self).__init__()
est la même chose que d'appeler A.__init__(self)
mais vous auriez tort. Dans la situation ci-dessus, super(B,self).__init__()
appelle effectivement C.__init__(self)
.
Bon sang de bonsoir, B
ne sait rien de C
et pourtant super(B,self)
sait qu'il faut appeler C
's __init__
? La raison en est que self.__class__.mro()
contient C
. En d'autres termes, self
(ou dans le cas précédent, foo
) connaît C
.
Alors faites attention - les deux ne sont pas fongibles. Ils peuvent donner des résultats très différents.
Utilisation de super
comporte des pièges. Il faut un niveau considérable de coordination entre toutes les classes du diagramme d'héritage. (Elles doivent, par exemple, soit avoir la même signature d'appel de __init__
puisque tout __init__
ne saurait pas quel autre __init__
super
pourrait appeler le prochain, ou sinon utiliser **kwargs
.) En outre, vous devez être cohérent dans l'utilisation de super
partout. Sautez-la une fois (comme dans l'exemple ci-dessus) et vous allez à l'encontre de l'objectif de l'opération. super
. Voir le lien pour d'autres pièges.
Si vous avez un contrôle total sur votre hiérarchie de classes, ou si vous évitez les diamants de l'héritage, alors il n'y a pas besoin de super
.
Il n'y a pas de pénalité en l'état, bien que votre exemple soit quelque peu erroné. Dans le premier exemple, ce devrait être
super(SubClass, instance).method(args) # Sub, not SuperClass
et cela m'amène à citer le Documentation Python :
Il existe deux cas d'utilisation typiques pour
super
. Dans une hiérarchie de classes avec héritage simple,super
peut être utilisé pour faire référence aux classes parentes sans les nommer explicitement, ce qui rend le code plus facile à maintenir. Cette utilisation est très proche de l'utilisation desuper
dans d'autres langages de programmation.Le deuxième cas d'utilisation consiste à prendre en charge l'héritage multiple coopératif dans un environnement d'exécution dynamique. Ce cas d'utilisation est unique à Python et ne se retrouve pas dans les langages compilés statiquement ou les langages qui ne supportent que l'héritage simple. Cela permet d'implémenter des "diagrammes en diamant" où plusieurs classes de base implémentent la même méthode. Une bonne conception exige que cette méthode ait la même signature d'appel dans tous les cas (parce que l'ordre des appels est déterminé au moment de l'exécution, parce que cet ordre s'adapte aux changements dans la hiérarchie des classes, et parce que cet ordre peut inclure des classes sœurs qui sont inconnues avant l'exécution).
En fait, en utilisant la première méthode, vous n'avez pas besoin de coder en dur votre classe parentale dans les hiérarchies à classe unique, et vous ne pouvez pas vraiment faire ce que vous voulez (de manière efficace) en utilisant la seconde méthode lorsque vous utilisez l'héritage multiple.
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.