Question

J'essaie d'apprendre la fonction super () en Python.

Je pensais bien le comprendre avant de revenir à cet exemple (2.6) et de me retrouver coincé.

http://www.cafepy.com/article/python_attributes_and_methods/python_attributes_and_methods.html#super-with-classmethod-example

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "test.py", line 9, in do_something
    do_something = classmethod(do_something)
TypeError: unbound method do_something() must be called with B instance as first argument (got nothing instead)
>>>

Ce n'était pas ce à quoi je m'attendais lorsque j'ai lu cette ligne juste avant l'exemple:

Si nous utilisons une méthode de classe, nous n'avons pas d'instance avec laquelle appeler Super. Heureusement pour nous, super fonctionne même avec un type comme second argument. --- Le type peut être passé directement à super comme indiqué ci-dessous.

Ce qui est exactement ce que Python me dit n'est pas possible en disant que do_something () devrait être appelé avec une instance de B.

Était-ce utile?

La solution

Parfois, les textes doivent être lus davantage pour la saveur de l'idée que pour les détails. C'est l'un de ces cas.

Dans la page associée , exemples 2.5 , 2.6 et 2.7 doivent tous utiliser la même méthode, do_your_stuff . (Autrement dit, do_something doit être remplacé par do_your_stuff .)

En outre, a souligné Ned Deily , A.do_your_stuff doit être un méthode de classe.

class A(object):
    @classmethod
    def do_your_stuff(cls):
        print 'This is A'

class B(A):
    @classmethod
    def do_your_stuff(cls):
        super(B, cls).do_your_stuff()

B.do_your_stuff()

super (B, cls) .do_your_stuff renvoie une méthode liée (voir note de bas de page 2 ). Puisque cls a été passé en tant que second argument de super () , c'est cls qui est lié à la méthode renvoyée. En d'autres termes, cls est passé en tant que premier argument de la méthode do_your_stuff () de la classe A.

Pour réitérer: super (B, cls) .do_your_stuff () fait en sorte que la méthode A de do_your_stuff soit appelé avec cls passé en tant que premier argument. Pour que cela fonctionne, A 's do_your_stuff doit être une méthode de classe. La page liée ne mentionne pas cela, mais c’est définitivement le cas.

PS. do_something = classmethod (do_something) est l'ancienne méthode de création d'une méthode de classe. La nouvelle méthode consiste à utiliser le décorateur @classmethod.

Notez que super (B, cls) ne peut pas être remplacé par super (cls, cls) . Cela pourrait conduire à des boucles infinies. Par exemple,

class A(object):
    @classmethod
    def do_your_stuff(cls):
        print('This is A')

class B(A):
    @classmethod
    def do_your_stuff(cls):
        print('This is B')
        # super(B, cls).do_your_stuff()  # CORRECT
        super(cls, cls).do_your_stuff()  # WRONG

class C(B):
    @classmethod
    def do_your_stuff(cls):
        print('This is C')
        # super(C, cls).do_your_stuff()  # CORRECT
        super(cls, cls).do_your_stuff()  # WRONG

C.do_your_stuff()

lève RuntimeError: la profondeur de récursivité maximale est dépassée lors de l'appel d'un objet Python .

Si cls est C , super (cls, cls) recherche C.mro () la classe qui vient après C .

In [161]: C.mro()
Out[161]: [__main__.C, __main__.B, __main__.A, object]

Puisque cette classe est B , lorsque cls est C , super (cls, cls) .do_your_stuff () toujours appelle B.do_your_stuff . Puisque super (cls, cls) .do_votre_stuff () est appelé à l'intérieur de B.do_votre_stuff , vous appelez B.do_your_stuff dans une boucle infinie. .

En Python3, le sous forme de 0 code argument de super a été ajouté pour que super (B, cls) puisse être remplacé par super () , et Python3 déterminera dans le contexte que super ( ) dans la définition de classe B doit être équivalent à super (B, cls) .

Mais super (cls, cls) (ou pour des raisons similaires, super (type (self), self) ) n'est jamais correct.

Autres conseils

En Python 3, vous pouvez ignorer la spécification des arguments pour super ,

.
class A:
    @classmethod
    def f(cls):
        return "A's f was called."

class B(A):
    @classmethod
    def f(cls):
        return super().f()

assert B.f() == "A's f was called."

J'ai mis à jour l'article pour le rendre un peu plus clair: Attributs et méthodes Python # Super

Votre exemple d'utilisation de la méthode de classe ci-dessus montre ce qu'est une méthode de classe: elle passe la classe elle-même au lieu de l'instance comme premier paramètre. Mais vous n’avez même pas besoin d’une instance pour appeler la méthode, par exemple:

>>> class A(object):
...     @classmethod
...     def foo(cls):
...         print cls
... 
>>> A.foo() # note this is called directly on the class
<class '__main__.A'>

L'exemple de la page Web semble fonctionner tel que publié. Avez-vous également créé une méthode do_something pour la super-classe, mais sans en faire une méthode de classe? Quelque chose comme ça va vous donner cette erreur:

>>> class A(object):
...     def do_something(cls):
...         print cls
... #   do_something = classmethod(do_something)
... 
>>> class B(A):
...     def do_something(cls):
...         super(B, cls).do_something()
...     do_something = classmethod(do_something)
... 
>>> B().do_something()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in do_something
TypeError: unbound method do_something() must be called with B instance as first argument (got nothing instead)

Je pense avoir compris le sens maintenant grâce à ce site magnifique et à cette belle communauté.

Si cela ne vous dérange pas, corrigez-moi si je me trompe sur les méthodes de classe (que j'essaie maintenant de bien comprendre):


# EXAMPLE #1
>>> class A(object):
...     def foo(cls):
...             print cls
...     foo = classmethod(foo)
... 
>>> a = A()
>>> a.foo()
# THIS IS THE CLASS ITSELF (__class__)
class '__main__.A'

# EXAMPLE #2
# SAME AS ABOVE (With new @decorator)
>>> class A(object):
...     @classmethod
...     def foo(cls):
...             print cls
... 
>>> a = A()
>>> a.foo()
class '__main__.A'

# EXAMPLE #3
>>> class B(object):
...     def foo(self):
...             print self
... 
>>> b = B()
>>> b.foo()
# THIS IS THE INSTANCE WITH ADDRESS (self)
__main__.B object at 0xb747a8ec
>>>

J'espère que cette illustration montre ..

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top