Verwenden von Super mit einer Klassenmethode

83

Ich versuche die super () Funktion in Python zu lernen.

Ich dachte, ich hätte es verstanden, bis ich über dieses Beispiel (2.6) kam und feststeckte.

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

Es war nicht das, was ich erwartet hatte, als ich diese Zeile direkt vor dem Beispiel las:

Wenn wir eine Klassenmethode verwenden, haben wir keine Instanz, mit der wir super aufrufen können. Zum Glück funktioniert super sogar mit einem Typ als zweitem Argument. --- Der Typ kann wie unten gezeigt direkt an super übergeben werden.

Genau das, was Python mir sagt, ist nicht möglich, wenn man sagt, dass do_something () mit einer Instanz von B aufgerufen werden soll.

dza
quelle
Mögliches Duplikat des Aufrufs der Klassenmethode einer Basisklasse in Python
Mr_and_Mrs_D

Antworten:

98

Manchmal müssen Texte eher für den Geschmack der Idee als für die Details gelesen werden. Dies ist einer dieser Fälle.

Auf der verlinkten Seite sollten die Beispiele 2.5, 2.6 und 2.7 alle eine Methode verwenden do_your_stuff. (Das heißt, do_somethingsollte geändert werden do_your_stuff.)

Darüber hinaus muss, wie Ned Deily betonte , A.do_your_stuffeine Klassenmethode sein.

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 gibt eine gebundene Methode zurück (siehe Fußnote 2 ). Da clsals zweites Argument an übergeben wurde super(), wird es clsan die zurückgegebene Methode gebunden. Mit anderen Worten, clswird als erstes Argument an die Methode do_your_stuff()der Klasse A übergeben.

Um es noch einmal zu wiederholen: Bewirkt, super(B, cls).do_your_stuff()dass Adie do_your_stuffMethode von aufgerufen wird, wobei clsals erstes Argument übergeben wird. Damit das funktioniert, muss A's do_your_stuffeine Klassenmethode sein. Die verlinkte Seite erwähnt das nicht, aber das ist definitiv der Fall.

PS. do_something = classmethod(do_something)ist die alte Art, eine Klassenmethode zu erstellen. Die neue (er) Möglichkeit besteht darin, den @ classmethod-Dekorator zu verwenden.


Beachten Sie, dass super(B, cls)nicht durch ersetzt werden kann super(cls, cls). Dies könnte zu Endlosschleifen führen. Zum Beispiel,

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

wird erhöhen RuntimeError: maximum recursion depth exceeded while calling a Python object.

Wenn clsist C, dann super(cls, cls)sucht C.mro()für die Klasse , die nach kommt C.

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

Da diese Klasse B, wann clsist C, super(cls, cls).do_your_stuff() immer aufruft B.do_your_stuff. Da super(cls, cls).do_your_stuff()innen aufgerufen wird B.do_your_stuff, rufen Sie am Ende B.do_your_stuffin einer Endlosschleife auf.

In Python3 wurde die 0-Argument-Form vonsuper hinzugefügt, damit super(B, cls)sie durch ersetzt werden kann super(), und Python3 wird aus dem Kontext herausfinden, dass super()in der Definition von class Bgleich sein sollte super(B, cls).

Aber unter keinen Umständen ist super(cls, cls)(oder aus ähnlichen Gründen super(type(self), self)) jemals richtig.

unutbu
quelle
Gibt es Fallstricke bei der Verwendung von Super (cls, cls)?
George Moutsopoulos
2
@ GeorgeMoutsopoulos: super(cls, cls)ist mit ziemlicher Sicherheit ein Fehler. Ich habe den obigen Beitrag bearbeitet, um zu erklären, warum.
Unutbu
39

In Python 3 können Sie die Angabe von Argumenten für Folgendes überspringen 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."
Tankobot
quelle
Diese Antwort könnte auf eine andere Frage verschoben werden, wenn sich diese spezielle Frage speziell mit Python 2 befasst (es ist schwer zu sagen, da die verlinkte Website CafePy nicht mehr verfügbar ist).
Tankobot
Es funktioniert nicht für mich in einem meiner Projekte. Es gibtRuntimeError: super(): no arguments
Madtyn
4

Ich habe den Artikel aktualisiert, um ihn etwas klarer zu gestalten: Python-Attribute und -Methoden # Super

Ihr Beispiel mit der obigen Klassenmethode zeigt, was eine Klassenmethode ist - sie übergibt die Klasse selbst anstelle der Instanz als ersten Parameter. Sie benötigen jedoch nicht einmal eine Instanz, um die Methode aufzurufen, z.

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

Das Beispiel von der Webseite scheint wie veröffentlicht zu funktionieren. Haben Sie auch eine do_somethingMethode für die Oberklasse erstellt, diese aber nicht zu einer Klassenmethode gemacht? So etwas gibt Ihnen diesen Fehler:

>>> 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)
Ned Deily
quelle
Nein, meine A-Klasse sieht folgendermaßen aus: Klasse A (Objekt): def do_your_stuff (self): print "das ist A" Muss "Klasse A" so sein, wie Sie es gepostet haben? (mit do_something = classmethod (do_something))? Ich habe das Gefühl, dass das Dokument nichts darüber erzählt hat ..
dza
1
Der Sinn des superAufrufs in der B do_something-Methode besteht darin, eine Methode dieses Namens in einer ihrer Oberklassen aufzurufen. Wenn es in A (oder in Object) keine gibt, schlägt der Aufruf von B (). Do_something () mit fehl super object has no attribute do_something. ~ unutbu weist zu Recht darauf hin, dass das Beispiel im Dokument fehlerhaft ist.
Ned Deily
0

Ich denke, ich habe den Punkt jetzt dank dieser schönen Seite und der schönen Gemeinschaft verstanden.

Wenn es Ihnen nichts ausmacht, korrigieren Sie mich bitte, wenn ich mich in Bezug auf Klassenmethoden irre (die ich jetzt zu verstehen versuche):


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

Ich hoffe diese Abbildung zeigt ..

dza
quelle
Für eine Erklärung der Klassenmethoden finden Sie diese möglicherweise hilfreich: stackoverflow.com/questions/1669445/…
unutbu