Herança e Substituindo __init__ em python

129

Eu estava lendo 'Dive Into Python' e no capítulo sobre classes, dá este exemplo:

class FileInfo(UserDict):
    "store file metadata"
    def __init__(self, filename=None):
        UserDict.__init__(self)
        self["name"] = filename

O autor diz que, se você deseja substituir o __init__método, deve chamar explicitamente o pai __init__com os parâmetros corretos.

  1. E se essa FileInfoclasse tivesse mais de uma classe ancestral?
    • Preciso chamar explicitamente todos os __init__métodos das classes ancestrais ?
  2. Além disso, eu tenho que fazer isso com qualquer outro método que eu queira substituir?
liewl
fonte
3
Observe que Sobrecarregar é um conceito separado de Substituir.
Dana the Sane

Respostas:

158

O livro é um pouco datado em relação às chamadas de subclasse-superclasse. Também é um pouco antiquado no que diz respeito à subclasse de classes internas.

Parece que hoje em dia:

class FileInfo(dict):
    """store file metadata"""
    def __init__(self, filename=None):
        super(FileInfo, self).__init__()
        self["name"] = filename

Observe o seguinte:

  1. Podemos subclasse diretamente classes built-in, como dict, list, tuple, etc.

  2. A superfunção controla o rastreamento das superclasses dessa classe e a chamada de funções nelas adequadamente.

S.Lott
fonte
5
devo procurar um livro / tutorial melhor?
liewl
2
Então, no caso de herança múltipla, o super () rastreia todas elas em seu nome?
Dana
dict .__ init __ (self), na verdade, mas não há nada errado com isso - a chamada super (...) apenas fornece uma sintaxe mais consistente. (Não tenho certeza como ele funciona para herança múltipla, eu acho que só pode encontrar uma superclasse de inicialização )
David Z
4
A intenção de super () é que ele lide com herança múltipla. A desvantagem é que, na prática, a herança múltipla ainda quebra com muita facilidade (consulte < fuhm.net/super-harmful ).
Sth
2
Sim, no caso de várias classes de herança e base receberem argumentos de construtor, geralmente você se chama pelos construtores manualmente.
Torsten Marek
18

Em cada classe da qual você precisa herdar, é possível executar um loop de cada classe que precisa ser iniciada no início da classe filho ... um exemplo que pode ser copiado pode ser melhor compreendido ...

class Female_Grandparent:
    def __init__(self):
        self.grandma_name = 'Grandma'

class Male_Grandparent:
    def __init__(self):
        self.grandpa_name = 'Grandpa'

class Parent(Female_Grandparent, Male_Grandparent):
    def __init__(self):
        Female_Grandparent.__init__(self)
        Male_Grandparent.__init__(self)

        self.parent_name = 'Parent Class'

class Child(Parent):
    def __init__(self):
        Parent.__init__(self)
#---------------------------------------------------------------------------------------#
        for cls in Parent.__bases__: # This block grabs the classes of the child
             cls.__init__(self)      # class (which is named 'Parent' in this case), 
                                     # and iterates through them, initiating each one.
                                     # The result is that each parent, of each child,
                                     # is automatically handled upon initiation of the 
                                     # dependent class. WOOT WOOT! :D
#---------------------------------------------------------------------------------------#



g = Female_Grandparent()
print g.grandma_name

p = Parent()
print p.grandma_name

child = Child()

print child.grandma_name
Code Bug
fonte
2
Não parece que o loop Child.__init__for seja necessário. Quando o removo do exemplo, a criança ainda imprime "Vovó". O init dos avós não é tratado pela Parentclasse?
Adam
4
Acho que os init dos avós já são tratados pelo init dos pais, não são?
johk95
15

Você realmente não precisa chamar os __init__métodos da (s) classe (s) base (s), mas geralmente deseja fazê-lo, porque as classes base farão algumas inicializações importantes necessárias para o funcionamento dos métodos das demais classes.

Para outros métodos, isso depende de suas intenções. Se você deseja apenas adicionar algo ao comportamento das classes base, convém chamar o método das classes base adicionalmente ao seu próprio código. Se você deseja alterar fundamentalmente o comportamento, talvez não chame o método da classe base e implemente toda a funcionalidade diretamente na classe derivada.

sth
fonte
4
Por questões de integridade técnica, algumas classes, como threading.Thread, lançarão erros gigantescos se você tentar evitar chamar o init do pai .
21715 David Berger
5
Acho tudo isso "você não precisa chamar o construtor da base" extremamente irritante. Você não precisa chamá-lo em nenhum idioma que eu conheça. Todos eles estragam (ou não) da mesma maneira, não inicializando membros. Sugerir para não inicializar as classes base está errado de várias maneiras. Se uma classe não precisar de inicialização agora, precisará no futuro. O construtor faz parte da interface da estrutura de classe / construção de linguagem e deve ser usado corretamente. Seu uso correto é chamá-lo em algum momento no construtor de seu derivado. Então faça.
precisa saber é o seguinte
2
"Sugerir para não inicializar as classes base está errado de várias maneiras." Ninguém sugeriu não inicializar a classe base. Leia a resposta com atenção. É tudo sobre intenção. 1) Se você deseja deixar a lógica de inicialização da classe base como está, não substitui o método init na sua classe derivada. 2) Se você deseja estender a lógica init da classe base, defina seu próprio método init e chame o método init da classe base. 3) Se você deseja substituir a lógica de inicialização da classe base, defina seu próprio método init sem chamar o da classe base.
Wombatonfire 9/07
4

Se a classe FileInfo tiver mais de uma classe ancestral, você definitivamente deve chamar todas as suas funções __init __ (). Você também deve fazer o mesmo com a função __del __ (), que é um destruidor.

moinudin
fonte
2

Sim, você deve ligar __init__para cada classe pai. O mesmo vale para funções, se você estiver substituindo uma função que existe nos dois pais.

vezult
fonte