maneira correta de usar super (passagem de argumento)


99

Então eu estava seguindo Super Considered Harmful do Python e fui testar seus exemplos.

No entanto, o Exemplo 1-3 , que deveria mostrar a maneira correta de chamar superao lidar com __init__métodos que esperam argumentos diferentes, não funciona.

Isso é o que eu recebo:

~ $ python example1-3.py 
MRO: ['E', 'C', 'A', 'D', 'B', 'object']
E arg= 10
C arg= 10
A
D arg= 10
B
Traceback (most recent call last):
  File "Download/example1-3.py", line 27, in <module>
    E(arg=10)
  File "Download/example1-3.py", line 24, in __init__
    super(E, self).__init__(arg, *args, **kwargs)
  File "Download/example1-3.py", line 14, in __init__
    super(C, self).__init__(arg, *args, **kwargs)
  File "Download/example1-3.py", line 4, in __init__
    super(A, self).__init__(*args, **kwargs)
  File "Download/example1-3.py", line 19, in __init__
    super(D, self).__init__(arg, *args, **kwargs)
  File "Download/example1-3.py", line 9, in __init__
    super(B, self).__init__(*args, **kwargs)
TypeError: object.__init__() takes no parameters

Parece que por objectsi só viola uma das melhores práticas mencionadas no documento, que é os métodos que o uso superdeve aceitar *argse **kwargs.

Obviamente, o Sr. Knight esperava que seus exemplos funcionassem, então isso é algo que foi alterado nas versões recentes do Python? Verifiquei 2.6 e 2.7 e falha em ambos.

Então, qual é a maneira correta de lidar com esse problema?


2
Minha maneira preferida é: Hierarquias de herança planas e simples.
milimoose

19
Você também deve ler "Python's super () considerado super" para obter uma visão equilibrada :)
Björn Pollex

@ BjörnPollex: Obrigado! O seu link fornece uma resposta para a pergunta: Você pode escrever uma "classe root" que herda de object, e torna-se a chamada objecté __init__corretamente.
cha0site

4
Observe que o __init__on objectignora silenciosamente todos os parâmetros do Python 2.5. Isso mudou no Python 2.6.
Wilfred Hughes

1
@Wilfred: Ei, obrigado por responder à pergunta real! Agora eu sei porque o ensaio está desatualizado!
cha0site

Respostas:


103

Às vezes, duas classes podem ter alguns nomes de parâmetros em comum. Nesse caso, você não pode **kwargsretirar os pares de valores-chave ou removê-los *args. Em vez disso, você pode definir uma Baseclasse que object, ao contrário , absorve / ignora argumentos:

class Base(object):
    def __init__(self, *args, **kwargs): pass

class A(Base):
    def __init__(self, *args, **kwargs):
        print "A"
        super(A, self).__init__(*args, **kwargs)

class B(Base):
    def __init__(self, *args, **kwargs):
        print "B"
        super(B, self).__init__(*args, **kwargs)

class C(A):
    def __init__(self, arg, *args, **kwargs):
        print "C","arg=",arg
        super(C, self).__init__(arg, *args, **kwargs)

class D(B):
    def __init__(self, arg, *args, **kwargs):
        print "D", "arg=",arg
        super(D, self).__init__(arg, *args, **kwargs)

class E(C,D):
    def __init__(self, arg, *args, **kwargs):
        print "E", "arg=",arg
        super(E, self).__init__(arg, *args, **kwargs)

print "MRO:", [x.__name__ for x in E.__mro__]
E(10)

rendimentos

MRO: ['E', 'C', 'A', 'D', 'B', 'Base', 'object']
E arg= 10
C arg= 10
A
D arg= 10
B

Observe que, para que isso funcione, Basedeve ser a penúltima turma do MRO.


4
Não deveria Baseligar super(Base, self).__init__()?
cha0site

4
Para que isso funcione, Basedeve vir no final do MRO (exceto para object). Ligar object.__init__não faz nada, então não há problema em não ligar super(Base, self).__init__(). Na verdade, acho que pode ser mais claro não incluir, super(Base, self).__init__()para esclarecer, o ponto que Baseé o fim da linha.
unutbu

25

Se você vai ter muita herança (esse é o caso aqui), sugiro que você passe todos os parâmetros usando **kwargs, e então popeles logo após usá-los (a menos que você precise deles nas classes superiores).

class First(object):
    def __init__(self, *args, **kwargs):
        self.first_arg = kwargs.pop('first_arg')
        super(First, self).__init__(*args, **kwargs)

class Second(First):
    def __init__(self, *args, **kwargs):
        self.second_arg = kwargs.pop('second_arg')
        super(Second, self).__init__(*args, **kwargs)

class Third(Second):
    def __init__(self, *args, **kwargs):
        self.third_arg = kwargs.pop('third_arg')
        super(Third, self).__init__(*args, **kwargs)

Essa é a maneira mais simples de resolver esse tipo de problema.

third = Third(first_arg=1, second_arg=2, third_arg=3)

6

Conforme explicado no super () do Python, considerado super , uma maneira é fazer com que a classe coma os argumentos de que necessita e passe o resto adiante. Assim, quando a cadeia de chamada chega object, todos os argumentos foram comidos e object.__init__serão chamados sem argumentos (como esperado). Portanto, seu código deve ser assim:

class A(object):
    def __init__(self, *args, **kwargs):
        print "A"
        super(A, self).__init__(*args, **kwargs)

class B(object):
    def __init__(self, *args, **kwargs):
        print "B"
        super(B, self).__init__(*args, **kwargs)

class C(A):
    def __init__(self, arg, *args, **kwargs):
        print "C","arg=",arg
        super(C, self).__init__(*args, **kwargs)

class D(B):
    def __init__(self, arg, *args, **kwargs):
        print "D", "arg=",arg
        super(D, self).__init__(*args, **kwargs)

class E(C,D):
    def __init__(self, arg, *args, **kwargs):
        print "E", "arg=",arg
        super(E, self).__init__(*args, **kwargs)

print "MRO:", [x.__name__ for x in E.__mro__]
E(10, 20, 30)

Então, sobre o quê E(arg = 10)? Não gosto desse método, preciso conhecer o MRO com antecedência para poder usá-lo. O outro método em que escrevo uma "classe raiz" que herda objectparece muito mais limpo.
cha0site

Este método é útil se as funções chamadas não compartilham nomes de argumentos. Eu admito que não tenho muita experiência prática com super, então esta abordagem pode ser bastante teórica.
Björn Pollex
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.