Использование super с методом класса

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

Это было не то, что я ожидал, когда прочитал эту строку прямо перед примером:

Если мы используем метод класса, у нас нет экземпляра для вызова super. К счастью для нас, super работает даже с типом в качестве второго аргумента. --- Тип можно передать непосредственно в super, как показано ниже.

Это именно то, что говорит мне Python, невозможно, говоря, что do_something () должен вызываться с экземпляром B.

дза
источник
Возможный дубликат метода вызова базового класса в Python
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()класса A.

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

PS. do_something = classmethod(do_something)это старый способ создания classmethod. Новый (er) способ - использовать декоратор @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)) никогда не является правильным.

Unutbu
источник
Есть ли подводные камни при использовании super (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 больше не доступен).
Tankobot
У меня это не работает в моем проекте. Это даетRuntimeError: super(): no arguments
madtyn 05
4

Я обновил статью, чтобы сделать ее немного понятнее: Атрибуты и методы Python # Super

В приведенном выше примере с использованием метода класса показано, что такое метод класса - он передает сам класс вместо экземпляра в качестве первого параметра. Но вам даже не нужен экземпляр для вызова метода, например:

>>> 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 выглядит так: class A (object): def do_your_stuff (self): print "this is A" Обязательно ли иметь "class A" в том виде, в котором вы его разместили? (с do_something = classmethod (do_something))? Мне кажется, что в документе об этом ничего не говорится ..
dza
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