Python in a Nutshell (2nd Edition) 책 에는
기존 스타일 클래스를 사용 하여 메서드가 고전적인 해결 순서로 해결되는
방법과 새로운 순서 와 어떻게 다른지 보여주는 예제 가 있습니다.
새 스타일로 예제를 다시 작성하여 동일한 예제를 시도했지만 결과는 이전 스타일 클래스에서 얻은 것과 다르지 않습니다. 예제를 실행하는 데 사용하는 파이썬 버전은 2.5.2입니다. 다음은 그 예입니다.
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__
호출이 instance.amethod()
인쇄 Base1
되지만 새로운 스타일의 클래스로 MRO에 대한 나의 이해에 따라 출력이되어야합니다 Base3
. 호출 Derived.__mro__
은 다음을 인쇄합니다.
(<class '__main__.Derived'>, <class '__main__.Base2'>, <class '__main__.Base1'>, <class '__main__.Base3'>, <type 'object'>)
새로운 스타일 클래스를 사용하는 MRO에 대한 이해가 잘못되었는지 또는 감지 할 수없는 어리석은 실수를하고 있는지 확실하지 않습니다. MRO를 더 잘 이해하도록 도와주세요.
답변
레거시 클래스와 새로운 스타일 클래스의 해결 순서 간의 결정적인 차이는 동일한 조상 클래스가 “순진한”깊이 우선 접근 방식에서 두 번 이상 발생할 때 발생합니다. 예를 들어 “다이아몬드 상속”사례를 고려하십시오.
>>> class A: x = 'a'
...
>>> class B(A): pass
...
>>> class C(A): x = 'c'
...
>>> class D(B, C): pass
...
>>> D.x
'a'
여기서 레거시 스타일의 해상도 순서는 D-B-A-C-A입니다. 따라서 Dx를 찾을 때 A는 해결 순서의 첫 번째 기준이므로 C에서 정의를 숨 깁니다.
>>> class A(object): x = 'a'
...
>>> class B(A): pass
...
>>> class C(A): x = 'c'
...
>>> class D(B, C): pass
...
>>> D.x
'c'
>>>
여기서 새로운 스타일의 순서는 다음과 같습니다.
>>> D.__mro__
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>,
<class '__main__.A'>, <type 'object'>)
와 A
, 한 번만와 그 서브 클래스의 모든 후 확인 순서에 와서 강제로 너무 재 지정 (회원의 C의 재정, 즉 것을 x
) 실제로 현명하게 작동합니다.
이것이 구식 클래스를 피해야하는 이유 중 하나입니다. “다이아몬드와 같은”패턴을 사용한 다중 상속은 새 스타일에서는 제대로 작동하지 않습니다.
답변
Python의 메서드 확인 순서는 실제로 다이아몬드 패턴을 이해하는 것보다 더 복잡합니다. 실제로 이해 하려면 C3 선형화를 살펴보십시오 . 주문을 추적하기 위해 메서드를 확장 할 때 print 문을 사용하는 것이 정말 도움이된다는 것을 발견했습니다. 예를 들어,이 패턴의 출력이 무엇이라고 생각하십니까? (참고 : ‘X’는 노드가 아닌 두 개의 교차 모서리라고 가정하고 ^는 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
ABDCEFG를 받았습니까?
x = A()
x.m()
많은 시행 착오 끝에 C3 선형화에 대한 비공식적 인 그래프 이론 해석을 다음과 같이 생각해 냈습니다.
이 예를 고려하십시오.
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()
답변
당신이 얻는 결과는 정확합니다. Base3
to의 기본 클래스를 변경 Base1
하고 클래식 클래스의 동일한 계층과 비교해 보십시오 .
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()
이제 다음을 출력합니다.
Base3
Base1
자세한 정보는 이 설명 을 읽으십시오 .
답변
메서드 확인은 너비 우선이 아니라 깊이 우선이기 때문에 이러한 동작을 볼 수 있습니다. Dervied의 상속은 다음과 같습니다.
Base2 -> Base1
/
Derived - Base3
그래서 instance.amethod()
- Base2를 확인하고 amethod를 찾지 못했습니다.
- Base2가 Base1에서 상속되었는지 확인하고 Base1을 확인합니다. Base1에는이
amethod
있으므로 호출됩니다.
이것은에 반영됩니다 Derived.__mro__
. 단순히 반복하고 Derived.__mro__
찾는 방법을 찾으면 중지하십시오.