156 votes

manière correcte d'utiliser super (passage d'arguments)

Donc je suivais Le super python est considéré comme dangereux et est allé tester ses exemples.

Cependant, Exemple 1-3 qui est censé montrer la manière correcte d'appeler super lors de la manipulation __init__ les méthodes qui attendent des arguments différents, ne fonctionnent pas.

Voilà ce que je reçois :

~ $ python example1-3.py 
MRO: ['E', 'C', 'A', 'D', 'B', 'object']
E arg= 10
C arg= 10
A
D arg= 10
B
Traceback (most recent call last):
  File "Download/example1-3.py", line 27, in <module>
    E(arg=10)
  File "Download/example1-3.py", line 24, in __init__
    super(E, self).__init__(arg, *args, **kwargs)
  File "Download/example1-3.py", line 14, in __init__
    super(C, self).__init__(arg, *args, **kwargs)
  File "Download/example1-3.py", line 4, in __init__
    super(A, self).__init__(*args, **kwargs)
  File "Download/example1-3.py", line 19, in __init__
    super(D, self).__init__(arg, *args, **kwargs)
  File "Download/example1-3.py", line 9, in __init__
    super(B, self).__init__(*args, **kwargs)
TypeError: object.__init__() takes no parameters

Il semble que object elle-même viole l'une des meilleures pratiques mentionnées dans le document, qui est que les méthodes qui utilisent super doit accepter *args y **kwargs .

De toute évidence, M. Knight s'attendait à ce que ses exemples fonctionnent, alors est-ce quelque chose qui a été modifié dans les versions récentes de Python ? J'ai vérifié les versions 2.6 et 2.7, et ça ne fonctionne pas sur les deux.

Quelle est donc la bonne façon de traiter ce problème ?

158voto

unutbu Points 222216

Parfois, deux classes peuvent avoir des noms de paramètres en commun. Dans ce cas, vous ne pouvez pas extraire les paires clé-valeur de l'outil **kwargs ou les retirer de *args . Au lieu de cela, vous pouvez définir un Base qui, contrairement aux object , absorbe/ignore les arguments :

class Base(object):
    def __init__(self, *args, **kwargs): pass

class A(Base):
    def __init__(self, *args, **kwargs):
        print "A"
        super(A, self).__init__(*args, **kwargs)

class B(Base):
    def __init__(self, *args, **kwargs):
        print "B"
        super(B, self).__init__(*args, **kwargs)

class C(A):
    def __init__(self, arg, *args, **kwargs):
        print "C","arg=",arg
        super(C, self).__init__(arg, *args, **kwargs)

class D(B):
    def __init__(self, arg, *args, **kwargs):
        print "D", "arg=",arg
        super(D, self).__init__(arg, *args, **kwargs)

class E(C,D):
    def __init__(self, arg, *args, **kwargs):
        print "E", "arg=",arg
        super(E, self).__init__(arg, *args, **kwargs)

print "MRO:", [x.__name__ for x in E.__mro__]
E(10)

donne

MRO: ['E', 'C', 'A', 'D', 'B', 'Base', 'object']
E arg= 10
C arg= 10
A
D arg= 10
B

Notez que pour que cela fonctionne, Base doit être l'avant-dernière classe de l'ORM.

41voto

juliomalegria Points 6281

Si vous allez avoir beaucoup d'héritages (c'est le cas ici), je vous suggère de passer tous les paramètres à l'aide de **kwargs et ensuite pop immédiatement après les avoir utilisés (sauf si vous en avez besoin dans les classes supérieures).

class First(object):
    def __init__(self, *args, **kwargs):
        self.first_arg = kwargs.pop('first_arg')
        super(First, self).__init__(*args, **kwargs)

class Second(First):
    def __init__(self, *args, **kwargs):
        self.second_arg = kwargs.pop('second_arg')
        super(Second, self).__init__(*args, **kwargs)

class Third(Second):
    def __init__(self, *args, **kwargs):
        self.third_arg = kwargs.pop('third_arg')
        super(Third, self).__init__(*args, **kwargs)

C'est la façon la plus simple de résoudre ce genre de problèmes.

third = Third(first_arg=1, second_arg=2, third_arg=3)

13voto

Björn Pollex Points 41424

Comme expliqué dans Le super() de Python a considéré super Une façon d'y parvenir est de faire en sorte que la classe consomme les arguments dont elle a besoin et transmette le reste. Ainsi, lorsque la chaîne d'appel atteint object tous les arguments ont été consommés, et object.__init__ sera appelé sans arguments (comme il s'y attend). Votre code devrait donc ressembler à ceci :

class A(object):
    def __init__(self, *args, **kwargs):
        print "A"
        super(A, self).__init__(*args, **kwargs)

class B(object):
    def __init__(self, *args, **kwargs):
        print "B"
        super(B, self).__init__(*args, **kwargs)

class C(A):
    def __init__(self, arg, *args, **kwargs):
        print "C","arg=",arg
        super(C, self).__init__(*args, **kwargs)

class D(B):
    def __init__(self, arg, *args, **kwargs):
        print "D", "arg=",arg
        super(D, self).__init__(*args, **kwargs)

class E(C,D):
    def __init__(self, arg, *args, **kwargs):
        print "E", "arg=",arg
        super(E, self).__init__(*args, **kwargs)

print "MRO:", [x.__name__ for x in E.__mro__]
E(10, 20, 30)

Prograide.com

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.

Powered by:

X