Использование супер с методом класса - PullRequest
62 голосов
/ 30 ноября 2009

Я пытаюсь изучить функцию 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.

Ответы [ 5 ]

78 голосов
/ 30 ноября 2009

Иногда тексты должны быть прочитаны больше для вкуса идеи, а не для деталей. Это один из таких случаев.

На связанной странице в примерах 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 do_your_stuff должен быть методом класса. Связанная страница не упоминает об этом, но это определенно так.

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

12 голосов
/ 12 ноября 2017

В 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."
3 голосов
/ 03 декабря 2009

Я обновил статью, чтобы сделать ее более понятной: Атрибуты и методы 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 голосов
/ 30 ноября 2009

Пример с веб-страницы работает как опубликованный. Вы также создали метод 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)
0 голосов
/ 30 ноября 2009

Я думаю, что теперь понял смысл благодаря этому прекрасному сайту и прекрасному сообществу.

Если вы не возражаете, пожалуйста, поправьте меня, если я ошибаюсь в методах обучения (что я сейчас пытаюсь полностью понять):


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

Я надеюсь, что эта иллюстрация показывает ..

...