Pergunta

Estou executando o Python 2.5, então essa pergunta pode não se aplicar ao Python 3. Quando você faz uma hierarquia de classe de diamante usando a herança múltipla e cria um objeto da classe mais derivada, o Python faz a coisa certa (TM). Ele chama o construtor para a classe mais derivada e, em seguida, suas classes pais listadas da esquerda para a direita e depois do avô. Estou familiarizado com o Python Mro; Essa não é a minha pergunta. Estou curioso para saber como o objeto retornou do Super realmente consegue se comunicar com chamadas de super nas classes dos pais, a ordem correta. Considere este código de exemplo:

#!/usr/bin/python

class A(object):
    def __init__(self): print "A init"

class B(A):
    def __init__(self):
        print "B init"
        super(B, self).__init__()

class C(A):
    def __init__(self):
        print "C init"
        super(C, self).__init__()

class D(B, C):
    def __init__(self):
        print "D init"
        super(D, self).__init__()

x = D()

O código faz a coisa intuitiva, imprime:

D init
B init
C init
A init

No entanto, se você comentar a chamada para super na função init de B, nem a função init de A nem C de C será chamada. Isso significa que o chamado de B para Super está ciente da existência de C na hierarquia geral da classe. Sei que Super retorna um objeto proxy com um operador GET sobrecarregado, mas como o objeto devolvido pela Super na definição init de D comunica a existência de C ao objeto devolvido pela Super na definição init de B? As informações de que as chamadas subsequentes do super uso são armazenadas no próprio objeto? Se sim, por que super em vez de si mesmo não é mais?

EDIT: Jekke apontou com razão que não é mais verdadeiro porque Super é um atributo da classe, não uma instância da classe. Conceitualmente, isso faz sentido, mas na prática Super não é um atributo da classe também! Você pode testar isso no intérprete fazendo duas classes A e B, onde B herda de A e chamando dir(B). Não tem super ou __super__ atributos.

Foi útil?

Solução

Eu forneci um monte de links abaixo, que respondem à sua pergunta com mais detalhes e mais precisamente do que posso esperar. No entanto, darei uma resposta à sua pergunta em minhas próprias palavras, para economizar algum tempo. Vou colocar em pontos -

  1. Super é uma função incorporada, não um atributo.
  2. Todo modelo (classe) em Python tem um __mro__ atributo, que armazena a ordem de resolução do método dessa instância específica.
  3. Cada chamada para super é do formulário super (tipo [, objeto-ou tipo]). Vamos supor que o segundo atributo seja um objeto no momento.
  4. No ponto de partida de Super Calls, o objeto é do tipo de classe derivada (diga dc).
  5. Super procura métodos que correspondam (no seu caso __init__) nas aulas do MRO, após a classe especificada como o primeiro argumento (neste caso, classes após DC).
  6. Quando o método de correspondência é encontrado (digamos na aula BC1), é chamado.
    (Este método deve usar super, por isso estou assumindo que ele - veja o super bacana de Python, mas não pode ser usado - link abaixo) que o método causa uma pesquisa no MRO da classe do objeto para o próximo método, à direita de BC1.
  7. Enxágue a lavagem Repita até que todos os métodos sejam encontrados e chamados.

Explicação para o seu exemplo

 MRO: D,B,C,A,object  
  1. super(D, self).__init__() é chamado. isinstance (self, d) => true
  2. Procurar por Próximo método no MRO nas aulas à direita de D.

    B.__init__ encontrado e chamado


  1. B.__init__ chamadas super(B, self).__init__().

    isinstance (self, b) => false
    isinstance (self, d) => true

  2. Assim, o MRO é o mesmo, mas a pesquisa continua à direita do objeto B IE C, A, é pesquisada uma a uma. Nas próximas __init__ encontrado é chamado.

  3. E assim por diante.

Uma explicação de super
http://www.python.org/download/releases/2.2.3/descrintro/#cooperation
Coisas a serem observadas ao usar super
http://fuhm.net/super-harmful/
Algoritmo MRO de Pythons:
http://www.python.org/download/releases/2.3/mro/
Docs Super:
http://docs.python.org/library/functions.html
A parte inferior desta página tem uma boa seção em Super:
http://docstore.mik.ua/orelly/other/python/0596001886_pythonian-chp-5-sect-2.html

Espero que isso ajude a esclarecer.

Outras dicas

Mude seu código para isso e acho que vai explicar as coisas (presumivelmente super está olhando para onde, digamos, B está no __mro__?):

class A(object):
    def __init__(self):
        print "A init"
        print self.__class__.__mro__

class B(A):
    def __init__(self):
        print "B init"
        print self.__class__.__mro__
        super(B, self).__init__()

class C(A):
    def __init__(self):
        print "C init"
        print self.__class__.__mro__
        super(C, self).__init__()

class D(B, C):
    def __init__(self):
        print "D init"
        print self.__class__.__mro__
        super(D, self).__init__()

x = D()

Se você executar, você verá:

D init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
B init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
C init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
A init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)

Também vale a pena conferir O Super do Python é bacana, mas você não pode usá -lo.

apenas adivinhando:

self Em todos os quatro métodos se referem ao mesmo objeto, isto é, de classe D. Então, em B.__init__(), a chamada para super(B,self) conhece toda a ascendência de diamante de self E tem que buscar o método de 'depois' B. Nesse caso, é o C classe.

super() conhece o cheio Hierarquia de classe. Isso é o que acontece no Init de B:

>>> super(B, self)
<super: <class 'B'>, <D object>>

Isso resolve a questão central,

Como o objeto devolvido pela Super na definição init de D comunica a existência de C ao objeto retornado pela Super na definição init de B?

Ou seja, na definição init de B, self é uma instância de D, e assim comunica a existência de C. Por exemplo C pode ser encontrado em type(self).__mro__.

A resposta de Jacob mostra como entender o problema, enquanto a Batbrat mostra os detalhes e a HRR vai direto ao ponto.

Uma coisa que eles não cobrem (pelo menos não explicar) de sua pergunta é este ponto:

No entanto, se você comentar a chamada para super na função init de B, nem a função init de A nem C de C será chamada.

Para entender isso, mude o código de Jacob para imprimir a pilha no início de A, como abaixo:

import traceback

class A(object):
    def __init__(self):
        print "A init"
        print self.__class__.__mro__
        traceback.print_stack()

class B(A):
    def __init__(self):
        print "B init"
        print self.__class__.__mro__
        super(B, self).__init__()

class C(A):
    def __init__(self):
        print "C init"
        print self.__class__.__mro__
        super(C, self).__init__()

class D(B, C):
    def __init__(self):
        print "D init"
        print self.__class__.__mro__
        super(D, self).__init__()

x = D()

É um pouco surpreendente ver que BLinha super(B, self).__init__() está realmente ligando C.__init__(), Como C não é uma base de base de B.

D init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
B init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
C init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
A init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
  File "/tmp/jacobs.py", line 31, in <module>
    x = D()
  File "/tmp/jacobs.py", line 29, in __init__
    super(D, self).__init__()
  File "/tmp/jacobs.py", line 17, in __init__
    super(B, self).__init__()
  File "/tmp/jacobs.py", line 23, in __init__
    super(C, self).__init__()
  File "/tmp/jacobs.py", line 11, in __init__
    traceback.print_stack()

Isso acontece porque super (B, self) não é 'Chamando a versão de Baseclass de B de B __init__'. Em vez disso, é 'chamando __init__ na primeira aula à direita de B que está presente em self's __mro__ E isso tem esse atributo.

Então, se você Comente a chamada para super na função init de B, a pilha de métodos vai parar B.__init__, e nunca chegará C ou A.

Para resumir:

  • Independentemente de qual classe está se referindo a ela, self é sempre uma referência à instância, e seu __mro__ e __class__ permanece constante
  • Super () encontra o método olhando para as classes que estão à direita do atual __mro__. Enquanto o __mro__ permanece constante, o que acontece é que ele é pesquisado como uma lista, não como uma árvore ou um gráfico.

Nesse último ponto, observe que o nome completo do algoritmo do MRO é Linearização da superclasse C3. Isto é, nutre essa estrutura em uma lista. Quando o diferente super() As chamadas acontecem, elas estão efetivamente iterando essa lista.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top