>  기사  >  백엔드 개발  >  Python의 isinstance 상속 관계 오버로드 문제에 대한 간략한 논의

Python의 isinstance 상속 관계 오버로드 문제에 대한 간략한 논의

不言
不言원래의
2018-05-04 14:21:241399검색

이 글은 주로 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) 동작을 오버로드하기 위한 조건을 요약했습니다.

  1. x 객체는 C에서 직접 인스턴스화할 수 없습니다.

  2. C 클래스는 메타클래스를 지정합니다. __instancecheck__는 지정된 메타클래스 클래스에 정의되어 있습니다.

  3. 테스트 코드:

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

에서 read_excel에 대한 자세한 설명

위 내용은 Python의 isinstance 상속 관계 오버로드 문제에 대한 간략한 논의의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.