이 글은 주로 Python의 isinstance 상속 관계 오버로드 문제를 소개합니다. 이제 특정 참조 값을 가지고 있습니다. 필요한 친구들이 참조할 수 있습니다.
상속 관계를 판단하세요
생성 메소드 isinstance(object, classinfo)는 객체가 특정 클래스의 인스턴스인지 여부를 확인할 수 있습니다. 이 관계는 직접적이거나 간접적이거나 추상적일 수 있습니다.
인스턴스 검사는 오버로드가 허용됩니다. customizing-instance-and-subclass-checks 문서를 참조하세요. PEP 3119의 설명에 따르면:
여기서 제안된 기본 메커니즘은 내장 함수 isinstance() 및 issubclass()의 오버로드를 허용하는 것입니다. 오버로딩은 다음과 같이 작동합니다. isinstance(x, C) 호출은 먼저 여부를 확인합니다. C. __instancecheck__가 존재하며, 그렇다면 일반 구현 대신 C.__instancecheck__(x)를 호출합니다.
이 의미는 감지를 위해 isinstance(x, C)를 호출할 때 먼저 C가 존재하는지 확인한다는 것입니다. __instancecheck__, 존재하는 경우 C.__instancecheck__(x)를 호출하고 반환된 결과는 인스턴스 감지 결과이며 기본 판단 방법은 없습니다.
이 방법은 덕 타이핑을 확인하는 데 도움이 되며 코드로 테스트했습니다.
class Sizeable(object): def __instancecheck__(cls, instance): print("__instancecheck__ call") return hasattr(instance, "__len__") class B(object): pass b = B() print(isinstance(b, Sizeable)) # output:False
False만 인쇄되고 __instancecheck__는 호출되지 않습니다. 무슨 일이야?
실행 중인 __instancecheck__
문서가 명확하게 작성되지 않은 것을 볼 수 있습니다. 문제를 찾기 위해 isinstance 소스 코드에서 추적을 시작합니다.
[abstract.c] int PyObject_IsInstance(PyObject *inst, PyObject *cls) { _Py_IDENTIFIER(__instancecheck__); PyObject *checker; /* Quick test for an exact match */ if (Py_TYPE(inst) == (PyTypeObject *)cls) return 1; .... }
Py_TYPE(inst) == (PyTypeObject *)cls 이것은 type(inst) is cls와 동일한 빠른 일치 방법입니다. 이 빠른 방법이 성공적으로 일치하면 __instancecheck__를 확인하지 않습니다. 따라서 C.__instancecheck__ 존재에 대한 문서의 우선순위 확인이 잘못되었습니다. 계속해서 소스 코드를 살펴보세요:
/* We know what type's __instancecheck__ does. */ if (PyType_CheckExact(cls)) { return recursive_isinstance(inst, cls); }
Expand 매크로 PyType_CheckExact:
[object.h] #define PyType_CheckExact(op) (Py_TYPE(op) == &PyType_Type)
즉, cls는 유형에서 직접 구성된 클래스이고, 그러면 판단 언어가 설정됩니다. 클래스 선언에 명시된 메타클래스를 제외하고는 기본적으로 타입별로 직접 구성된다. 테스트 코드를 통해 판단이 확립되었음을 알고 recursive_isinstance를 입력합니다. 하지만 이 함수에서 __instancecheck__에 대한 코드는 찾지 못했습니다. recursive_isinstance의 판단 논리는 대략 다음과 같습니다.
def recursive_isinstance(inst, cls): return pyType_IsSubtype(inst, cls) def pyType_IsSubtype(a, b): for mro in a.__mro__: if mro is b: return True return False
는 __mro__ 상속 순서로 판단됩니다. PyObject_IsInstance 함수로 돌아가서 살펴보세요:
if (PyTuple_Check(cls)) { ... }
instance(x, C)의 두 번째 매개변수가 튜플일 때, 처리 방법은 PyObject_IsInstance(inst, item)를 재귀적으로 호출하는 것입니다. 계속 읽기:
checker = _PyObject_LookupSpecial(cls, &PyId___instancecheck__); if (checker != NULL) { res = PyObject_CallFunctionObjArgs(checker, inst, NULL); ok = PyObject_IsTrue(res); return ok; }
분명히 여기가 __instancecheck__를 얻는 곳입니다. 이 지점까지 확인 프로세스를 수행하려면 정의된 클래스에서 Metaclass를 지정해야 합니다. 남은 것은 _PyObject_LookupSpecial을 추적하는 것입니다:
[typeobject.c] PyObject * _PyObject_LookupSpecial(PyObject *self, _Py_Identifier *attrid) { PyObject *res; res = _PyType_LookupId(Py_TYPE(self), attrid); // 有回调的话处理回调 // ... return res; }
는 Py_TYPE(self)를 취합니다. 이는 __instancecheck__가 지정된 메타클래스에 정의되어야 함을 의미합니다.
요약
지금까지 isinstance(x, C) 동작을 오버로드하기 위한 조건을 요약했습니다.
x 객체는 C에서 직접 인스턴스화할 수 없습니다.
C 클래스는 메타클래스를 지정합니다. __instancecheck__는 지정된 메타클래스 클래스에 정의되어 있습니다.
테스트 코드:
class MetaSizeable(type): def __instancecheck__(cls, instance): print("__instancecheck__ call") return hasattr(instance, "__len__") class Sizeable(metaclass=MetaSizeable): pass class B(object): pass b = B() print(isinstance(b, Sizeable)) # output: False print(isinstance([], Sizeable)) # output: True
문서가 약간 오래되었을 수 있습니다. 본 테스트 환경은 Python3.6입니다.
관련 권장 사항:Python 2.7 pandas
위 내용은 Python의 isinstance 상속 관계 오버로드 문제에 대한 간략한 논의의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!