__Init __ () deve chamar __init __ () da classe pai?

132

Eu estou acostumado que no Objective-C eu tenho essa construção:

- (void)init {
    if (self = [super init]) {
        // init class
    }
    return self;
}

O Python também deve chamar a implementação da classe pai __init__?

class NewClass(SomeOtherClass):
    def __init__(self):
        SomeOtherClass.__init__(self)
        # init class

Isso também é verdadeiro / falso para __new__()e__del__() ?

Edit: Há uma pergunta muito semelhante: Herança e Substituição __init__em Python

Georg Schölly
fonte
você alterou seu código significativamente. Eu posso entender que o original objectfoi um erro de digitação. Mas agora você nem sequer tem o supertítulo da sua pergunta.
SilentGhost 6/09/09
Eu apenas pensei que super é usado como um nome para a classe pai. Não achei que alguém pensaria na função. Sinto muito por qualquer mal-entendido.
Georg Schölly 6/09/09
Um porque não automática questão chamada super-: stackoverflow.com/questions/3782827/...
Ciro Santilli郝海东冠状病六四事件法轮功

Respostas:

67

No Python, chamar a superclasse ' __init__é opcional. Se você chamá-lo, também é opcional usar o superidentificador ou nomear explicitamente a superclasse:

object.__init__(self)

No caso de um objeto, a chamada do super método não é estritamente necessária, pois o super método está vazio. O mesmo para __del__.

Por outro lado, pois __new__, você deve realmente chamar o super método e usar seu retorno como o objeto recém-criado - a menos que queira explicitamente retornar algo diferente.

Martin v. Löwis
fonte
Portanto, não há convenção de apenas chamar a implementação de super?
Georg Schölly 6/09/09
5
Nas classes de estilo antigo, você só poderia chamar de super init se a super classe realmente tivesse um init definido (o que geralmente não ocorre). Portanto, as pessoas normalmente pensam em chamar super método, em vez de fazê-lo por princípio.
Martin v. Löwis 6/09/09
1
Se a sintaxe em python fosse tão simples quanto [super init], seria mais comum. Apenas um pensamento especulativo; a super construção no Python 2.x é um pouco estranha para mim.
U0b34a0f6ae 6/09/09
Aqui parece ser uma interessante (e, possivelmente, contradizendo) exemplo: bytes.com/topic/python/answers/... inicialização
mlvljr
"opcional", pois você não precisa chamá-lo, mas se não o chamar, ele não será chamado automaticamente.
McKay
140

Se você precisar que algo de super __init__seja feito além do que está sendo feito na classe atual, __init__,você deve chamá-lo você mesmo, pois isso não acontecerá automaticamente. Mas se você não precisa de nada do super, __init__,não precisa chamá-lo. Exemplo:

>>> class C(object):
        def __init__(self):
            self.b = 1


>>> class D(C):
        def __init__(self):
            super().__init__() # in Python 2 use super(D, self).__init__()
            self.a = 1


>>> class E(C):
        def __init__(self):
            self.a = 1


>>> d = D()
>>> d.a
1
>>> d.b  # This works because of the call to super's init
1
>>> e = E()
>>> e.a
1
>>> e.b  # This is going to fail since nothing in E initializes b...
Traceback (most recent call last):
  File "<pyshell#70>", line 1, in <module>
    e.b  # This is going to fail since nothing in E initializes b...
AttributeError: 'E' object has no attribute 'b'

__del__ é da mesma maneira (mas desconfie de confiar em __del__ finalizar - considere fazê-lo através da instrução with).

Eu raramente uso __new__. faço toda a inicialização em__init__.

Anon
fonte
3
A definição de classe D (C) deve ser corrigida assimsuper(D,self).__init__()
Eyquem
11
super () .__ o init __ () só funciona em Python 3. Em Python 2 Você precisa super (D, self) .__ o init __ ()
Jacinda
"Se você precisar de algo do init do super ..." - Esta é uma afirmação muito problemática, porque não se trata de a subclasse você / a precisar de "alguma coisa", mas se a classe base precisa de algo para ser válida instância da classe base e funcione corretamente. Como implementador da classe derivada, os internos da classe base são coisas que você não pode / não deve saber, e mesmo que saiba porque escreveu os dois ou os internos estão documentados, o design da base pode mudar no futuro e interromper-se devido a uma má gravação. classe derivada. Portanto, sempre verifique se a classe base foi totalmente inicializada.
Nick
105

Na resposta de Anon:
"Se você precisa fazer algo com super, __init__além do que está sendo feito na classe atual __init__, você deve chamá-lo você mesmo, pois isso não acontecerá automaticamente"

É incrível: ele está expondo exatamente o contrário do princípio da herança.


Não é que "algo do super __init__ (...) não aconteça automaticamente" , é que isso aconteceria automaticamente, mas isso não acontece porque a classe base ' __init__é substituída pela definição dos clas derivados__init__

Então, por que definir uma classe derivada ' __init__ , uma vez que substitui o que se destina quando alguém recorre à herança?

É porque é necessário definir algo que NÃO é feito na classe base ' __init__, e a única possibilidade de obter isso é colocar sua execução em uma __init__função de classe derivada' .
Em outras palavras, é preciso algo na classe base ' __init__além do que seria feito automaticamente na classe base' __init__se esta última não fosse substituída.
NÃO é o contrário.


Então, o problema é que as instruções desejadas presentes na classe base ' __init__não são mais ativadas no momento da instanciação. Para compensar essa inativação, é necessário algo especial: chamar explicitamente a classe base ' __init__, para MANTER , NÃO ADICIONAR, a inicialização executada pela classe base' __init__. É exatamente o que é dito no documento oficial:

Um método de substituição em uma classe derivada pode de fato querer estender, em vez de simplesmente substituir o método da classe base com o mesmo nome. Existe uma maneira simples de chamar o método da classe base diretamente: basta chamar BaseClassName.methodname (self, argumentos).
http://docs.python.org/tutorial/classes.html#inheritance

Essa é toda a história:

  • quando o objetivo é manter a inicialização executada pela classe base, que é pura herança, nada de especial é necessário, é preciso apenas evitar definir uma __init__função na classe derivada

  • quando o objetivo é SUBSTITUIR a inicialização realizada pela classe base, __init__deve ser definida na classe derivada

  • quando o objetivo é adicionar processos à inicialização executada pela classe base, uma classe derivada ' __init__ deve ser definida, compreendendo uma chamada explícita à classe base__init__


O que me surpreende no post de Anon não é apenas o fato de ele expressar o contrário da teoria da herança, mas o fato de cinco caras terem passado por isso aprovadas sem fazer barba e, além disso, não havia ninguém para reagir em dois anos. um tópico cujo assunto interessante deve ser lido com relativa frequência.

eyquem
fonte
1
Eu tinha certeza de que este post seria votado. Receio não ter muitas explicações sobre os motivos. É mais fácil diminuir o voto do que analisar um texto que parece incompreensível. Fiquei muito tempo tentando entender o post do Anon antes de finalmente perceber que ele era ilusoriamente escrito e pouco autoritário. Talvez possa ser interpretado como aproximadamente correto para alguém que conhece a herança; mas acho que é confusioning quando lido por alguém que tenha noções instáveis sobre herança, um assunto não tão clara como água da rocha em geral
Eyquem
2
"Eu tinha certeza de que este post seria votado ..." A questão principal é que você está um pouco atrasado para a festa, e a maioria das pessoas pode não ler além das primeiras respostas. Grande explicação por sinal +1
Gerrat 06/04
Ótima resposta é essa.
Trilarion
3
Você perdeu as palavras significativas "além" na frase que citou de Aaron. A declaração de Aaron está completamente correta e corresponde ao que você acaba dizendo.
GreenAsJade
1
Esta é a primeira explicação que fez o design do python fazer sentido.
Joseph Garvin
20

Editar : (após a alteração do código)
Não há como dizer se você precisa ou não chamar os pais __init__(ou qualquer outra função). A herança obviamente funcionaria sem essa ligação. Tudo depende da lógica do seu código: por exemplo, se tudo o que você __init__faz na classe pai, você pode simplesmente pular a classe filho__init__ completo.

considere o seguinte exemplo:

>>> class A:
    def __init__(self, val):
        self.a = val


>>> class B(A):
    pass

>>> class C(A):
    def __init__(self, val):
        A.__init__(self, val)
        self.a += val


>>> A(4).a
4
>>> B(5).a
5
>>> C(6).a
12
SilentGhost
fonte
Eu removi a super chamada do meu exemplo, gostaria de saber se alguém deve chamar a implementação de init da classe pai ou não.
Georg Schölly 6/09/09
você pode editar o título então. mas minha resposta ainda permanece.
SilentGhost 6/09/09
5

Não há regra rígida e rápida. A documentação para uma classe deve indicar se as subclasses devem chamar o método da superclasse. Às vezes, você deseja substituir completamente o comportamento da superclasse e, outras vezes, aumentá-lo - ou seja, chame seu próprio código antes e / ou depois de uma chamada de superclasse.

Atualização: a mesma lógica básica se aplica a qualquer chamada de método. Às vezes, os construtores precisam de consideração especial (pois geralmente configuram o estado que determina o comportamento) e os destruidores, porque são paralelos aos construtores (por exemplo, na alocação de recursos, por exemplo, conexões com o banco de dados). Mas o mesmo pode se aplicar, por exemplo, ao render()método de um widget.

Atualização adicional: O que é o OPP? Você quer dizer POO? Não - uma subclasse geralmente precisa saber algo sobre o design da superclasse. Não os detalhes da implementação interna - mas o contrato básico que a superclasse possui com seus clientes (usando classes). Isso não viola os princípios de POO de forma alguma. É por isso que protectedé um conceito válido no OOP em geral (embora não, é claro, no Python).

Vinay Sajip
fonte
Você disse que às vezes alguém poderia chamar código próprio antes da chamada da superclasse. Para fazer isso, é necessário conhecimento da implementação da classe pai, o que violaria o OPP.
Georg Schölly 6/09/09
4

OMI, você deve chamá-lo. Se sua superclasse é object, você não deveria, mas em outros casos, acho excepcional não chamá-lo. Como já foi respondido por outras pessoas, é muito conveniente que sua classe nem precise se substituir __init__, por exemplo, quando não possui um estado interno (adicional) para inicializar.

u0b34a0f6ae
fonte
2

Sim, você sempre deve chamar a classe base __init__explicitamente como uma boa prática de codificação. Esquecer de fazer isso pode causar problemas sutis ou erros de tempo de execução. Isso é verdade mesmo se__init__ não houver parâmetros. Isso é diferente de outras linguagens em que o compilador implicitamente chamaria o construtor da classe base para você. Python não faz isso!

O principal motivo para sempre chamar a classe base _init__ é que a classe base normalmente pode criar variável de membro e inicializá-la com os padrões. Portanto, se você não chamar init da classe base, nada desse código seria executado e você terminaria com a classe base que não possui variáveis ​​de membro.

Exemplo :

class Base:
  def __init__(self):
    print('base init')

class Derived1(Base):
  def __init__(self):
    print('derived1 init')

class Derived2(Base):
  def __init__(self):
    super(Derived2, self).__init__()
    print('derived2 init')

print('Creating Derived1...')
d1 = Derived1()
print('Creating Derived2...')
d2 = Derived2()

Isso imprime ..

Creating Derived1...
derived1 init
Creating Derived2...
base init
derived2 init

Execute este código .

Shital Shah
fonte