Pergunta

No livro Python in a Nutshell (2nd Edition) não é um exemplo que utiliza
aulas de estilo antigo para demonstrar como métodos são resolvidos em ordem de resolução clássica e
como ele é diferente com a nova ordem.

Eu tentei o mesmo exemplo, reescrevendo o exemplo no novo estilo, mas o resultado não é diferente do que o que foi obtido com as classes de estilo antigo. A versão python que estou usando para executar o exemplo é 2.5.2 Abaixo está o exemplo:.

class Base1(object):  
    def amethod(self): print "Base1"  

class Base2(Base1):  
    pass

class Base3(object):  
    def amethod(self): print "Base3"

class Derived(Base2,Base3):  
    pass

instance = Derived()  
instance.amethod()  
print Derived.__mro__  

A chamada instance.amethod() impressões Base1 , mas de acordo com o meu entendimento da MRO, com um novo estilo de aulas a saída deveria ter sido Base3 . A chamada Derived.__mro__ impressões:

(<class '__main__.Derived'>, <class '__main__.Base2'>, <class '__main__.Base1'>, <class '__main__.Base3'>, <type 'object'>)

Não estou certo se o meu entendimento de MRO com novas classes de estilo está incorreta ou que eu estou fazendo um erro bobo que eu não sou capaz de detectar. Por favor, me ajudar na melhor compreensão da MRO.

Foi útil?

Solução

A diferença crucial entre a ordem de resolução para o legado vs classes new-style vem quando a mesma classe ancestral ocorre mais de uma vez na abordagem em profundidade "ingênuo", - por exemplo, considere um caso "herança diamante":

>>> class A: x = 'a'
... 
>>> class B(A): pass
... 
>>> class C(A): x = 'c'
... 
>>> class D(B, C): pass
... 
>>> D.x
'a'

aqui, o legado de estilo, a ordem de resolução é D - B - A - C - R: Então, quando olhando para cima Dx, A é a primeira base na ordem de resolução para resolvê-lo, escondendo assim a definição em C. Enquanto:

>>> class A(object): x = 'a'
... 
>>> class B(A): pass
... 
>>> class C(A): x = 'c'
... 
>>> class D(B, C): pass
... 
>>> D.x
'c'
>>> 

aqui, novo estilo, a ordem é:

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

com A forçado a entrar em ordem de resolução apenas uma vez e depois de todas as suas subclasses, de modo que substitui (ou seja, substituição de C de x membro) realmente funcionar de forma sensata.

É uma das razões que as classes de estilo antigo deve ser evitado:. Herança múltipla com "diamond-like" padrões simplesmente não funciona de forma sensata com eles, enquanto ele faz com novo estilo

Outras dicas

ordem de resolução método de Python é realmente mais complexo do que apenas entender o padrão de diamante. Para realmente compreendê-lo, dê uma olhada C3 linearização . Descobri que realmente ajuda a usar declarações print ao estender métodos para controlar a ordem. Por exemplo, o que você acha que a saída deste padrão seria? (Nota: o 'X' é suposto ser duas bordas cruzamento, não um nó e ^ significa métodos que chamada super ())

class G():
    def m(self):
        print("G")

class F(G):
    def m(self):
        print("F")
        super().m()

class E(G):
    def m(self):
        print("E")
        super().m()

class D(G):
    def m(self):
        print("D")
        super().m()

class C(E):
    def m(self):
        print("C")
        super().m()

class B(D, E, F):
    def m(self):
        print("B")
        super().m()

class A(B, C):
    def m(self):
        print("A")
        super().m()


#      A^
#     / \
#    B^  C^
#   /| X
# D^ E^ F^
#  \ | /
#    G

Você conseguiu A B D C E F G?

x = A()
x.m()

Depois de muita experimentação um erro, eu vim com um gráfico de interpretação teoria informal de C3 linearização da seguinte forma: (. Alguém por favor deixe-me saber se isso é errado)

Veja este exemplo:

class I(G):
    def m(self):
        print("I")
        super().m()

class H():
    def m(self):
        print("H")

class G(H):
    def m(self):
        print("G")
        super().m()

class F(H):
    def m(self):
        print("F")
        super().m()

class E(H):
    def m(self):
        print("E")
        super().m()

class D(F):
    def m(self):
        print("D")
        super().m()

class C(E, F, G):
    def m(self):
        print("C")
        super().m()

class B():
    def m(self):
        print("B")
        super().m()

class A(B, C, D):
    def m(self):
        print("A")
        super().m()

# Algorithm:

# 1. Build an inheritance graph such that the children point at the parents (you'll have to imagine the arrows are there) and
#    keeping the correct left to right order. (I've marked methods that call super with ^)

#          A^
#       /  |  \
#     /    |    \
#   B^     C^    D^  I^
#        / | \  /   /
#       /  |  X    /   
#      /   |/  \  /     
#    E^    F^   G^
#     \    |    /
#       \  |  / 
#          H
# (In this example, A is a child of B, so imagine an edge going FROM A TO B)

# 2. Remove all classes that aren't eventually inherited by A

#          A^
#       /  |  \
#     /    |    \
#   B^     C^    D^
#        / | \  /  
#       /  |  X    
#      /   |/  \ 
#    E^    F^   G^
#     \    |    /
#       \  |  / 
#          H

# 3. For each level of the graph from bottom to top
#       For each node in the level from right to left
#           Remove all of the edges coming into the node except for the right-most one
#           Remove all of the edges going out of the node except for the left-most one

# Level {H}
#
#          A^
#       /  |  \
#     /    |    \
#   B^     C^    D^
#        / | \  /  
#       /  |  X    
#      /   |/  \ 
#    E^    F^   G^
#               |
#               |
#               H

# Level {G F E}
#
#         A^
#       / |  \
#     /   |    \
#   B^    C^   D^
#         | \ /  
#         |  X    
#         | | \
#         E^F^ G^
#              |
#              |
#              H

# Level {D C B}
#
#      A^
#     /| \
#    / |  \
#   B^ C^ D^
#      |  |  
#      |  |    
#      |  |  
#      E^ F^ G^
#            |
#            |
#            H

# Level {A}
#
#   A^
#   |
#   |
#   B^  C^  D^
#       |   |
#       |   |
#       |   |
#       E^  F^  G^
#               |
#               |
#               H

# The resolution order can now be determined by reading from top to bottom, left to right.  A B C E D F G H

x = A()
x.m()

O resultado que obtém é correta. Tente alterar classe base de Base3 para Base1 e comparar com a mesma hierarquia para as classes clássicas:

class Base1(object):
    def amethod(self): print "Base1"

class Base2(Base1):
    pass

class Base3(Base1):
    def amethod(self): print "Base3"

class Derived(Base2,Base3):
    pass

instance = Derived()
instance.amethod()


class Base1:
    def amethod(self): print "Base1"

class Base2(Base1):
    pass

class Base3(Base1):
    def amethod(self): print "Base3"

class Derived(Base2,Base3):
    pass

instance = Derived()
instance.amethod()

Agora ele produz:

Base3
Base1

Leia esta explicação para obter mais informações.

Você está vendo esse comportamento porque a resolução de método é em profundidade, e não em largura. de dervied aparência de herança como

         Base2 -> Base1
        /
Derived - Base3

Assim instance.amethod()

  1. Verifica Base2, não encontra aMethod.
  2. vê que Base2 herdou de Base1, e verifica Base1. Base1 tem uma amethod, por isso é chamado.

Isso se reflete na Derived.__mro__. Simplesmente iterar Derived.__mro__ e parar quando você encontrar o método que está sendo procurado.

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