web-dev-qa-db-fra.com

Python super méthode et alternatives d'appel

Je vois partout des exemples de méthodes que les super-classes devraient appeler:

super(SuperClass, instance).method(args)

Y a-t-il un inconvénient à faire:

SuperClass.method(instance, args)
55
IanSR

Considérez 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()

Ainsi, les classes forment un soi-disant diamant d'héritage:

    A
   / \
  B   C
   \ /
    D

L'exécution du code donne

Running D.__init__
Running B.__init__
Running A.__init__

C'est mauvais parce que le __init__ de C est ignoré. La raison en est que le __init__ de B appelle directement le __init__ de A.

Le but de super est de résoudre les diamants d'héritage . Si vous ne commentez pas

# super(B,self).__init__()

et 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, toutes les méthodes __init__ sont appelées. Notez 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 vous trompez. Dans la situation ci-dessus, super(B,self).__init__() appelle en fait C.__init__(self).

Holy smokes, B ne sait rien de C, et pourtant super(B,self) sait appeler le __init__ de C? La raison en est que self.__class__.mro() contient C. En d'autres termes, self (ou ci-dessus, foo) connaît C.

Soyez donc prudent - les deux ne sont pas fongibles. Ils peuvent donner des résultats très différents.

Utiliser supera des pièges. Il faut un niveau considérable de coordination entre toutes les classes du diagramme d'héritage. (Ils doivent, par exemple, avoir la même signature d'appel pour __init__, car tout __init__ particulier ne saurait pas quel autre __init__super pourrait appeler ensuite, ou bien tilisez **kwargs .) , vous devez être cohérent sur l'utilisation de super partout. Ignorez-le une fois (comme dans l'exemple ci-dessus) et vous allez à l'encontre de l'objectif de super. Voir le lien pour plus d'écueils.

Si vous avez un contrôle total sur votre hiérarchie de classe, ou si vous évitez les diamants d'héritage, il n'est pas nécessaire de super.

105
unutbu

Il n'y a pas de pénalité telle quelle, bien que votre exemple soit quelque peu erroné. Dans le premier exemple, il devrait être

super(SubClass, instance).method(args)  # Sub, not SuperClass

et cela m'amène à citer les documents Python :

Il existe deux cas d'utilisation typiques pour super. Dans une hiérarchie de classes avec héritage unique, super peut être utilisé pour faire référence aux classes parentes sans les nommer explicitement, rendant ainsi le code plus maintenable. Cette utilisation est étroitement parallèle à l'utilisation de super 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 trouve pas dans les langages compilés statiquement ou les langages qui ne prennent en charge que l'héritage unique. Cela permet d'implémenter des "diagrammes en losanges" où plusieurs classes de base implémentent la même méthode Une bonne conception dicte que cette méthode a 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 frères inconnues avant Durée).

Fondamentalement, en utilisant la première méthode, vous n'avez pas à coder en dur votre classe parent pour les hiérarchies à classe unique, et vous ne pouvez tout simplement pas vraiment faire ce que vous voulez (efficacement/efficacement) en utilisant la deuxième méthode lorsque vous utilisez plusieurs héritage.

8
Daniel DiPaolo