Використання супер із методом класу


83

Я намагаюся вивчити функцію super () у Python.

Я думав, що зрозумів це, поки не натрапив на цей приклад (2.6) і не виявив, що застряг.

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)
>>>

Це було не те, що я очікував, коли прочитав цей рядок безпосередньо перед прикладом:

Якщо ми використовуємо метод класу, у нас немає екземпляра для виклику супер. На наше щастя, супер працює навіть із типом як другим аргументом. --- Тип можна передати безпосередньо супер, як показано нижче.

Що саме те, що Python каже мені, неможливо, сказавши, що do_something () слід викликати з екземпляром B.


Можливий дублікат виклику
Mr_and_Mrs_D

Відповіді:


99

Іноді тексти доводиться читати більше заради смаку ідеї, а не для деталей. Це один із таких випадків.

В пов'язаної сторінці , приклади 2.5, 2.6 і 2.7 повинні все використовувати один з методів, do_your_stuff. (Тобто do_somethingслід змінити на do_your_stuff.)

Крім того, як зазначив Нед Дейлі , A.do_your_stuffце повинен бути метод класу.

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 повертає зв'язаний метод (див. виноску 2 ). Оскільки clsбуло передано як другий аргумент super(), він clsприв'язується до повернутого методу. Іншими словами, clsпередається як перший аргумент методу do_your_stuff()класу А.

Повторимо ще раз: super(B, cls).do_your_stuff()причини A«и do_your_stuffметод , який буде викликатися з clsпередається в якості першого аргументу. Для того, щоб це працювало, A's do_your_stuffповинен бути методом класу. На пов’язаній сторінці про це не згадується, але це, безумовно, так.

PS. do_something = classmethod(do_something)це старий спосіб створення методу класу. Новим (ер) способом є використання декоратора @classmethod.


Зверніть увагу, що super(B, cls)не можна замінити на super(cls, cls). Це може призвести до нескінченних циклів. Наприклад,

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()

підніме RuntimeError: maximum recursion depth exceeded while calling a Python object.

Якщо clsє C, то super(cls, cls)пошук C.mro()для класу , який приходить після C.

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

Оскільки цей клас B, коли clsє C, super(cls, cls).do_your_stuff() завжди дзвонить B.do_your_stuff. Оскільки super(cls, cls).do_your_stuff()викликається всередині B.do_your_stuff, ви в кінцевому підсумку закликаєте B.do_your_stuffв нескінченний цикл.

У Python3, то 0-аргумент формаsuper була додана , щоб super(B, cls)можна було б замінити super(), і python3 буде з'ясувати з контексту , що super()у визначенні class Bповинні бути еквівалентні super(B, cls).

Але ні за яких обставин ніколи super(cls, cls)(або з подібних причин super(type(self), self)) ніколи не є правильним.


Чи є підводні камені у використанні супер (cls, cls)?
Джордж Мутсопулос

2
@GeorgeMoutsopoulos: super(cls, cls)майже напевно помилка. Я відредагував пост вище, щоб пояснити, чому.
unutbu

39

У Python 3 ви можете пропустити зазначення аргументів для 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."

Цю відповідь можна перенести на інше питання, якщо це конкретне питання стосується конкретно Python 2 (важко сказати, оскільки веб-сайт, на який посилається, CafePy, більше не доступний).
Танкобот

Це не працює для мене в моєму проекті. Це даєRuntimeError: super(): no arguments
madtyn

4

Я оновив статтю, щоб зробити її зрозумілішою: атрибути та методи Python # Super

Ваш приклад використання classmethod вище показує, що таке метод класу - він передає сам клас замість екземпляра як перший параметр. Але вам навіть не потрібен екземпляр для виклику методу, наприклад:

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

2

Здається, приклад із веб-сторінки працює у виданому вигляді. Ви також створили do_somethingметод для суперкласу, але не зробили його методом класу? Щось подібне дасть вам таку помилку:

>>> 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)

Ні, мій клас A виглядає так: клас A (об'єкт): def do_your_stuff (self): print "this is A" Чи потрібно мати "клас A" таким, яким ви його розмістили? (з do_something = classmethod (do_something))? Я відчуваю, що в документі нічого про це не
сказано

1
Вся суть superвиклику в методі B do_something полягає у виклику методу з такою назвою в одному з його суперкласів. Якщо в A (або в Object) його немає, виклик B (). Do_something () не вдається виконати super object has no attribute do_something. ~ unutbu справедливо зазначає, що приклад у документі є несправним.
Нед Дейлі

0

Думаю, я зрозумів суть зараз завдяки цьому чудовому сайту та прекрасній спільноті.

Якщо ви не проти, виправте мене, якщо я помиляюся щодо методів занять (які я зараз намагаюся зрозуміти повністю):


# 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
>>>

Сподіваюся, ця ілюстрація показує ..


Для пояснення методи класу, можливо , ви знайдете це корисним: stackoverflow.com/questions/1669445 / ...
unutbu
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.