Usando super com um método de classe
-
08-07-2019 - |
Pergunta
Eu estou tentando aprender a função super () em Python.
Eu pensei que tinha uma compreensão de que até I veio este exemplo (2.6) e encontrei-me preso.
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)
>>>
Não foi o que eu esperava quando eu li esse direito linha antes o exemplo:
Se estamos usando um método de classe, não temos uma instância para chamar super com. Felizmente para nós, super funciona mesmo com um tipo como o segundo argumento. --- O tipo pode ser passado directamente para a super como mostrado abaixo.
Que é exatamente o Python me diz que não é possível, dizendo que do_something () deve ser chamado com uma instância de B.
Solução
Às vezes, os textos têm de ser ler mais para o sabor da idéia em vez de para os detalhes. Este é um desses casos.
ligada página , Exemplos 2,5 , 2.6 e 2.7 devem utilizam todos um método, do_your_stuff
. (Isto é, do_something
deve ser alterado para do_your_stuff
.)
Além disso, como Ned Deily apontou , A.do_your_stuff
tem que ser um método de 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
retorna um obrigado método (ver nota 2 ). Desde cls
foi passado como o segundo argumento para super()
, é cls
que fica ligado ao método retornou. Em outras palavras, cls
é passado como o primeiro argumento para o do_your_stuff()
método da classe A.
Para reiterar: super(B, cls).do_your_stuff()
provoca método A
de do_your_stuff
ser
chamado com cls
passado como o primeiro argumento. Para que isso funcione, de A
do_your_stuff
tem de ser um método de classe. A página de destino não menciona que,
mas isso é definitivamente o caso.
PS. do_something = classmethod(do_something)
é a velha maneira de fazer uma classmethod.
A nova forma (er) é usar o decorador @classmethod.
Note que super(B, cls)
não pode ser substituído por super(cls, cls)
. Fazer isso pode levar a loops infinitos. Por exemplo,
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()
elevará RuntimeError: maximum recursion depth exceeded while calling a Python object
.
Se cls
é C
, então pesquisas super(cls, cls)
C.mro()
para a classe que vem depois C
.
In [161]: C.mro()
Out[161]: [__main__.C, __main__.B, __main__.A, object]
Uma vez que a classe é B
, quando cls
é C
, super(cls, cls).do_your_stuff()
sempre chama B.do_your_stuff
. Desde super(cls, cls).do_your_stuff()
é chamado dentro B.do_your_stuff
, você acaba chamando B.do_your_stuff
em um loop infinito.
Em Python3, o forma 0 argumento de super
foi adicionado de modo super(B, cls)
poderia ser substituída por super()
, e Python3 irá descobrir a partir do contexto que super()
na definição de class B
deve ser equivalente para super(B, cls)
.
Mas, em nenhuma circunstância é super(cls, cls)
(ou por razões semelhantes, super(type(self), self)
) sempre correta.
Outras dicas
Em Python 3, você pode pular especificar argumentos para 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."
Eu atualizei o artigo para torná-lo um pouco mais claro: Python atributos e métodos # Super
O seu exemplo usando classmethod acima mostra o que um método de classe é - ele passa a própria classe em vez da instância como o primeiro parâmetro. Mas você não precisa mesmo de uma instância para chamar o método, por exemplo:.
>>> class A(object):
... @classmethod
... def foo(cls):
... print cls
...
>>> A.foo() # note this is called directly on the class
<class '__main__.A'>
O exemplo a partir da página web parece funcionar como publicado. Você criou um método do_something
para a superclasse tão bem, mas não torná-lo em um classmethod? Algo como isso vai lhe dar esse erro:
>>> 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)
Eu acho que eu entendi o ponto de agora, graças a este site beatiful e encantador comunidade.
Se você não se importa por favor me corrija se eu estiver errado em classmethods (que agora estou tentando entender completamente):
# 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
>>>
Espero Esta ilustração mostra ..