Python MRO의 동적 특성

8604 단어 ooppythonarchitecture
다음 클래스 관계를 상상해보십시오.

class Base:
    def chain(self):
        return 'Base'


class A(Base):
    ...


class B(Base):
    def chain(self):
        return f"{super().chain()} <- B"


class C(A, B):
    pass


class D(C):
    def chain(self):
        return f"{super().chain()} <- D"


chain 인스턴스에서 D를 호출하면 다음 문자열이 생성됩니다.

In [1]: d.chain()
Out[1]: 'Base <- B <- D'


다음 코드가 실행되면 어떻게 될까요?

In [2]: A.chain = A.chain


공격적이지 않죠? 이제 d.chain() 다시 전화해 보세요...

In [3]: d.chain()
Out[3]: 'Base <- D'

D 의 메서드 확인 순서(MRO, 메서드 및 속성을 확인할 때 Python이 찾는 클래스의 순서)를 살펴보겠습니다.

In [4]: D.mro()
Out[4]: [__main__.D, __main__.C, __main__.A, __main__.B, __main__.Base, object]

D.chain() 를 호출하면 Python은 이 목록을 살펴보고 chain가 클래스의 구성원으로 있는 첫 번째 인스턴스를 반환합니다. 이 예에서 Dchain 를 구현합니다.
D.chain가 차례로 super().chain()를 호출합니다. 파이썬은 무엇을 할 것인가? D에서 다음 클래스를 가져와 이 새 목록에서 chain를 찾으려고 합니다. C 구현하지 않습니다. 그렇지 않습니다 A . B 그렇지! 결과는 B.chain 반환하는 것과 비트 " <- D" 를 더한 것입니다.
B.chain는 super()를 다시 호출합니다... 우리는 우리가 무엇을 하는지 알고 있습니다. Base가 이를 구현하고 더 이상 super() 호출이 없습니다. 따라서 Base.chain 반환 "Base" , B.chain() 반환 "Base <- B"D.chain() 최종 반환 "Base <- B <- D" 있습니다.

그래서 우리가 A.chain = A.chain 후에 무슨 일이 일어나고 있습니까? B.chain()가 무시되는 이유는 무엇입니까? 무슨 일이 일어나고 있는지 분석해 봅시다. A의 MRO는?

In [5]: A.mro()
Out[5]: [__main__.A, __main__.Base, object]


아주 간단합니다. A.chain 하면 어떻게 되나요? 파이썬은 그것을 구현하지 않는 A 를 볼 것입니다. 그러나 Base는 그것을 구현하므로 이것이 사용할 구현입니다. 그러나 우리가 과제를 수행할 때 무슨 일이 벌어지고 있습니까?

Python은 이 할당을 오른쪽에서 왼쪽으로 평가합니다. A.chain = A.chain 그런 다음 "find chain for A"를 의미하며, 이는 Base.chain 를 반환합니다. 그러면 파이썬은 클래스 A에 chain라는 새 멤버를 효과적으로 생성합니다!

D의 MRO로 돌아가 보겠습니다.

[__main__.D, __main__.C, __main__.A, __main__.B, __main__.Base, object]


이 클래스가 생성된 방식으로 A 의 멤버는 B 전에 테스트됩니다! 해결 순서는 무엇을 의미합니까? D.chain()super().chain()를 호출하면 A의 새로 추가된 멤버를 잡아 호출합니다. 그리고 A 의 관점에서 구현은 Base.chain 호출이 없는 super() 와 동일합니다! 🤯

이것은 Python의 동적 특성으로 인해 발생합니다. The entry for super() in Python's documentation 이 문제에 대한 놀라운 설명과 Python의 특성으로 인해 고유한 사용 사례가 있는 방법이 있습니다.

Python의 다중 상속 기능과 동적 특성으로 인해 실제 메서드 확인 순서와 클래스 계층 구조는 런타임에만 알려지고 응용 프로그램 수명 동안 언제든지 변경될 수 있습니다.

수업 디자인은 협력적이어야 합니다. 위의 예에는 많은 문제가 있습니다but there is a simple set of rules that help designing collaborative classes.
  • super()에서 호출한 메서드가 있어야 합니다.
  • 메서드가 호출되고 해당 구현에 일치하는 인수 서명이 있어야 합니다.
  • 메서드가 발생할 때마다 super()를 사용해야 합니다.

  • 여기서 요점은 super()에 의해 호출된 메서드의 실제 구현은 런타임에만 알려져 있으며 정적으로 쉽게 정의할 수 없다는 것입니다.

    클래스를 공동으로 설계하고 런타임에 클래스의 변형을 관찰합니다.

    Python의 MRO에 대해 더 알고 싶으십니까? Python 문서의 이 놀라운 기사는 버전 2.3 이후에 알고리즘이 어떻게 작동하는지 소개합니다. Check it out !

    이것은 내 첫 번째 dev.to 게시물입니다. 이 문제를 개선하기 위해 제가 할 수 있는 일이 있으면 알려주세요!

    Adam ŚmigielskiUnsplash의 사진

    좋은 웹페이지 즐겨찾기