Python프로그래밍시니어 파이썬 개발자

Python 메타클래스에서 `isinstance()` 호출을 가로채는 특정 더블 언더 메서드는 무엇이며, 이 메서드가 후보 객체에서 임의의 속성에 접근할 경우 발생하는 무한 재귀 위험은 무엇인가요?

Hintsage AI 어시스턴트로 면접 통과

질문에 대한 답변.

isinstance(obj, cls)가 호출되면, Pythontype(cls)__instancecheck__(self, instance)를 정의하는지 확인합니다. 만약 존재한다면, 이 메타클래스 메서드는 멤버십을 결정하여 상속 없이 "가상 서브클래싱"을 가능하게 합니다. 위험은 구현을 통해 hasattr() 또는 점 속성 접근을 obj에서 사용할 경우 발생합니다. 만약 obj__getattr__ 또는 isinstance() 검사를 트리거하는 설명자를 구현하고 있다면, 메타클래스 메서드는 다시 진입하여 무한 재귀를 초래합니다.

실제 사례

우리는 플러그인이 명시적인 상속 없이 인터페이스를 만족해야 하는 검증 프레임워크를 설계했습니다. 우리는 Processor ABC를 정의하고 isinstance(plugin, Processor)pluginprocess 메서드를 노출하는 경우 성공하기를 원했습니다. 이는 외부 라이브러리를 위한 덕 타이핑을 지원합니다.

첫 번째 접근 방식은 모든 플러그인이 Processor를 상속받거나 Processor.register()를 호출해야 했습니다. 이는 타입 안전했지만 우리의 사용 사례에는 비실용적이었습니다; 이는 런타임에 생성된 클래스와 변경할 수 없는 타사 코드의 수정을 요구했습니다. 따라서 불신원천으로부터 동적 플러그인 발견을 지원하지 못했습니다.

두 번째 접근 방식은 hasattr(candidate, 'process')를 사용하여 Processor 메타클래스에 __instancecheck__를 구현했습니다. 유연하지만, 플러그인이 반환 타입을 isinstance로 검증하는 속성 데코레이터를 사용할 경우 RecursionError가 발생했으며, 이는 최초 호출이 반환되기 전에 메타클래스 메서드가 다시 호출된 것입니다.

세 번째 해결 방안으로는, 설명자 로직을 우회하기 위해 object.__getattribute__를 사용하여 __instancecheck__를 구현했습니다. type(candidate).__dict__.get('process')를 확인하고 호출이 가능한지 검증하여 사용자 정의 __getattr__ 또는 속성 부작용을 트리거하지 않도록 했습니다. 이는 재귀 위험을 제거하면서 동적 덕 타이핑을 유지하여 소스 수정 없이 수천 개의 이질적인 플러그인을 안전하게 통합할 수 있게 했습니다.

class MetaProcessor(type): def __instancecheck__(cls, candidate): # 안전함: __getattr__와 설명자를 우회 try: attr = type(candidate).__dict__.get('process') return callable(attr) except Exception: return False class Processor(metaclass=MetaProcessor): pass

후보들이 종종 놓치는 것


isinstance()가 첫 번째 인수 대신 두 번째 인수의 메타클래스를 참고하나요?

프로토콜은 확인되는 타입(cls)의 권한을 부여하며, 후보 객체(obj)에게는 부여하지 않습니다. __instancecheck__type(cls)에 배치함으로써, Python은 클래스 정의만이 그 멤버십 의미론을 제어하도록 보장합니다. 이는 객체들이 인스턴스 체크를 스푸핑하는 것을 방지하며, 타입이 자의적으로 자신의 인스턴스가 무엇인지 정의하여, 보안이 중요한 타입 체크의 무결성을 유지합니다.


__instancecheck____subclasscheck__의 관계는 무엇이며, 왜 일관성을 유지해야 하나요?

__instancecheck__isinstance()에 대해 객체를 검증하고, __subclasscheck__issubclass()에 대해 타입을 검증합니다. 만약 __instancecheck__가 가상 인스턴스(클래스에서 상속받지 않는 객체)를 수용한다면, 일반적으로 __subclasscheck__는 해당 가상 서브클래스를 수용해야 합니다. 이는 isinstance(obj, cls)issubclass(type(obj), cls)를 의미하는 불변성을 유지해야 합니다. 이를 위반할 경우, 인스턴스 체크가 통과된 후 서브클래스 관계를 확인할 때 일반적인 컨테이너 코드가 실패할 수 있습니다.


__instancecheck__ 내부에서 getattr()을 사용하는 것이 어떻게 특히 무한 재귀를 촉발시키며, object.__getattribute__와 비교하여 그 이유는 무엇인가요?

getattr(obj, 'name')는 전체 설명자 프로토콜을 호출하며, 속성이 누락된 경우 obj.__getattr__('name')를 호출할 수 있습니다. 만약 obj.__getattr__가 레이지 로딩, 로깅 또는 타입 강제 변환을 구현하고 있어 내부적으로 isinstance(obj, OurClass)를 호출하면, 메타클래스 __instancecheck__는 반환되기 전에 다시 진입하게 됩니다. 반면에, object.__getattribute__(obj, 'name')(또는 type(obj).__dict__를 검사하는 것은) __getattr__와 사용자 정의 설명자를 완전히 우회하며, 재귀를 유발할 수 있는 사용자 코드를 트리거하지 않고 원시 구현 세부정보에 액세스합니다.