Domanda

Sto cercando di imparare la funzione super () in Python.

Pensavo di averlo compreso fino a quando non ho trovato questo esempio (2.6) e mi sono ritrovato bloccato.

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

Non era quello che mi aspettavo quando ho letto questa riga proprio prima dell'esempio:

Se stiamo usando un metodo class, non abbiamo un'istanza con cui chiamare super. Fortunatamente per noi, il super funziona anche con un tipo come secondo argomento. --- Il tipo può essere passato direttamente a super come mostrato di seguito.

Il che è esattamente ciò che mi dice Python non è possibile dicendo che do_something () dovrebbe essere chiamato con un'istanza di B.

È stato utile?

Soluzione

A volte i testi devono essere letti più per il sapore dell'idea piuttosto che per i dettagli. Questo è uno di questi casi.

Nella pagina collegata , Esempi 2.5 , 2.6 e 2.7 dovrebbero usare tutti un metodo, do_your_stuff . (Cioè, do_something dovrebbe essere modificato in do_your_stuff .)

Inoltre, come ha sottolineato Ned Deily , A.do_your_stuff deve essere un metodo di classe.

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 restituisce un metodo associato (vedi footnote 2 ). Poiché cls è stato passato come secondo argomento a super () , è cls che viene associato al metodo restituito. In altre parole, cls viene passato come primo argomento al metodo do_your_stuff () di classe A.

Per ribadire: super (B, cls) .do_your_stuff () fa in modo che il metodo A di do_your_stuff sia chiamato con cls passato come primo argomento. Affinché funzioni, A do_your_stuff deve essere un metodo di classe. La pagina collegata non dice che, ma questo è definitivamente il caso.

PS. do_something = classmethod (do_something) è il vecchio modo di creare un metodo di classe. Il nuovo modo (er) è usare il decoratore @classmethod.


Nota che super (B, cls) non può essere sostituito da super (cls, cls) . Ciò potrebbe portare a cicli infiniti. Ad esempio,

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

genererà RuntimeError: superata la profondità massima di ricorsione durante la chiamata di un oggetto Python .

Se cls è C , quindi super (cls, cls) cerca C.mro () per la classe che viene dopo C .

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

Poiché quella classe è B , quando cls è C , super (cls, cls) .do_your_stuff () sempre chiama B.do_your_stuff . Poiché super (cls, cls) .do_your_stuff () viene chiamato all'interno di B.do_your_stuff , finisci per chiamare B.do_your_stuff in un ciclo infinito .

In Python3, il forma a 0 argomenti di super è stato aggiunto in modo che super (B, cls) potesse essere sostituito da super () e Python3 capirà dal contesto che super ( ) nella definizione di classe B dovrebbe essere equivalente a super (B, cls) .

Ma in nessun caso super (cls, cls) (o per ragioni simili, super (type (self), self) ) è sempre corretto.

Altri suggerimenti

In Python 3, puoi saltare gli argomenti specificando 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."

Ho aggiornato l'articolo per renderlo un po 'più chiaro: Attributi e metodi di Python # Super

Il tuo esempio che usa il metodo di classe sopra mostra cos'è un metodo di classe: passa la classe stessa invece dell'istanza come primo parametro. Ma non hai nemmeno bisogno di un'istanza per chiamare il metodo, ad esempio:

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

L'esempio della pagina web sembra funzionare come pubblicato. Hai creato un metodo do_something anche per la superclasse ma non lo hai trasformato in un metodo di classe? Qualcosa del genere ti darà quell'errore:

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

Penso di aver capito il punto ora grazie a questo bellissimo sito e alla bella comunità.

Se non ti dispiace per favore correggimi se sbaglio sui metodi di classe (che ora sto cercando di comprendere appieno):


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

Spero che questa illustrazione mostri ..

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top