Używanie super z metodą klasową


83

Próbuję nauczyć się funkcji super () w Pythonie.

Myślałem, że to rozumiem, dopóki nie doszedłem do tego przykładu (2.6) i utknąłem.

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

Nie tego się spodziewałem, czytając ten wiersz tuż przed przykładem:

Jeśli używamy metody klasowej, nie mamy instancji, z którą można by wywołać super. Na szczęście dla nas super działa nawet z typem jako drugim argumentem. --- Typ można przekazać bezpośrednio do super, jak pokazano poniżej.

Dokładnie to, co mówi mi Python, nie jest możliwe, mówiąc, że do_something () powinno zostać wywołane z instancją B.


Odpowiedzi:


99

Czasami teksty trzeba czytać bardziej ze względu na smak pomysłu niż szczegóły. To jest jeden z tych przypadków.

Na stronie , do której prowadzi łącze, w przykładach 2.5, 2.6 i 2.7 należy używać jednej metody, czyli do_your_stuff. (Oznacza to, że do_somethingnależy zmienić na do_your_stuff.)

Ponadto, jak zauważył Ned Deily , A.do_your_stuffmusi to być metoda klasowa.

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 zwraca powiązaną metodę (patrz przypis 2 ). Ponieważ clszostał przekazany jako drugi argument do super(), jest clson powiązany z zwróconą metodą. Innymi słowy, clszostaje przekazany jako pierwszy argument do metody do_your_stuff()klasy A.

Powtórzmy: super(B, cls).do_your_stuff()przyczyny A„s do_your_stuffmetodę można nazwać z clsprzekazywany jako pierwszy argument. Aby to do pracy, A„s do_your_stuffmusi być metoda klasy. Strona, do której prowadzi link, o tym nie wspomina, ale ostatecznie tak jest.

PS. do_something = classmethod(do_something)jest starym sposobem tworzenia metody klasowej. Nowym (nowym) sposobem jest użycie dekoratora @classmethod.


Pamiętaj, że super(B, cls)nie można tego zastąpić super(cls, cls). Może to prowadzić do nieskończonych pętli. Na przykład,

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

wzrośnie RuntimeError: maximum recursion depth exceeded while calling a Python object.

Jeśli clstak C, super(cls, cls)wyszukuje następną C.mro()klasę C.

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

Ponieważ ta klasa jest B, kiedy clsjest C, super(cls, cls).do_your_stuff() zawsze dzwoni B.do_your_stuff. Ponieważ super(cls, cls).do_your_stuff()jest wywoływany wewnątrz B.do_your_stuff, w końcu wywołujesz B.do_your_stuffw nieskończonej pętli.

W Pythonie3 została dodana 0-argumentowa forma argumentu,super więc super(B, cls)można ją zastąpić super(), a Python3 wykryje z kontekstu, który super()w definicji class Bpowinien być równoważny super(B, cls).

Ale w żadnym wypadku nie jest super(cls, cls)(ani z podobnych powodów super(type(self), self)) nigdy nie jest poprawne.


Czy są jakieś pułapki w używaniu super (cls, cls)?
George Moutsopoulos

2
@GeorgeMoutsopoulos: super(cls, cls)to prawie na pewno błąd. Zredagowałem powyższy post, aby wyjaśnić, dlaczego.
unutbu

39

W Pythonie 3 możesz pominąć określanie argumentów dla 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."

Tę odpowiedź można przenieść do innego pytania, jeśli to konkretne pytanie dotyczy konkretnie Pythona 2 (trudno to stwierdzić, ponieważ powiązana witryna internetowa CafePy nie jest już dostępna).
Tankobot

To nie działa dla mnie w moim projekcie. To dajeRuntimeError: super(): no arguments
madtyn

4

Zaktualizowałem artykuł, aby był nieco jaśniejszy: Atrybuty i metody Pythona # Super

Twój przykład wykorzystujący metodę classmethod powyżej pokazuje, czym jest metoda klasy - przekazuje klasę zamiast instancji jako pierwszy parametr. Ale nie potrzebujesz nawet instancji, aby wywołać metodę, na przykład:

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

2

Przykład ze strony internetowej wydaje się działać tak, jak został opublikowany. Czy stworzyłeś również do_somethingmetodę dla nadklasy, ale nie uczyniłeś z niej metody klasowej? Coś takiego da ci ten błąd:

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

Nie, moja klasa A wygląda tak: klasa A (obiekt): def do_your_stuff (self): print "to jest A" Czy konieczne jest posiadanie "klasy A" w takiej postaci, w jakiej ją wysłałeś? (z do_something = classmethod (do_something))? Wydaje mi się, że dokument nic o tym nie mówi…
dza

1
Głównym celem superwywołania metody B do_something jest wywołanie metody o tej nazwie w jednej z jej nadklas. Jeśli nie ma takiego w A (lub Object), wywołanie B (). Do_something () kończy się niepowodzeniem super object has no attribute do_something. ~ unutbu słusznie wskazuje, że przykład w dokumencie jest błędny.
Ned Deily

0

Myślę, że teraz zrozumiałem, o co chodzi, dzięki tej pięknej witrynie i wspaniałej społeczności.

Jeśli nie masz nic przeciwko, popraw mnie, jeśli się mylę w metodach klasowych (które teraz próbuję w pełni zrozumieć):


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

Mam nadzieję, że ta ilustracja pokazuje ...


Aby uzyskać wyjaśnienie metod klasowych, być może okaże się pomocny: stackoverflow.com/questions/1669445/…
unutbu
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.