Почему __instancecheck__ не всегда вызывается в зависимости от аргумента?
Есть этот код:
class Meta(type):
def __instancecheck__(self, instance):
print("__instancecheck__")
return True
class A(metaclass=Meta):
pass
a = A()
isinstance(a, A) # __instancecheck__ not called
isinstance([], A) # __instancecheck__ called
Почему __instancecheck__
вызывается для аргумента []
, но не для аргумента a
?
Ответы
Ответ 1
PyObject_IsInstance
выполняет быстрый тест для точного соответствия.
Objects/abstract.c
:
int
PyObject_IsInstance(PyObject *inst, PyObject *cls)
{
static PyObject *name = NULL;
/* Quick test for an exact match */
if (Py_TYPE(inst) == (PyTypeObject *)cls)
return 1;
// ...
Не нравится быстрый путь? вы можете попробовать это (на свой страх и риск):
>>> import __builtin__
>>> def isinstance(a, b):
... class tmp(type(a)):
... pass
... return __builtin__.isinstance(tmp(), b)
...
>>> __builtin__.isinstance(a, A)
True
>>> isinstance(a, A)
__instancecheck__
True
Ответ 2
Я думаю, что PEP, описывающий __instancecheck__()
, неисправен. PEP 3119 говорит:
Основной механизм, предлагаемый здесь, - это перегрузка встроенные функции isinstance() и issubclass(). Перегрузка работает следующим образом: вызов isststance (x, C) сначала проверяет, C.__instancecheck__
существует, и если это так, называет C.__instancecheck__(x)
вместо обычной реализации.
Вы можете написать:
class C:
def do_stuff(self):
print('hello')
C.do_stuff(C())
Итак, основываясь на приведенной выше цитате из PEP, вы должны иметь возможность писать
class C:
@classmethod
def __instancecheck__(cls, x):
print('hello')
C.__instancecheck__(C())
--output:--
hello
Но isinstance() не вызывает этот метод:
class C:
@classmethod
def __instancecheck__(cls, y):
print('hello')
x = C()
isinstance(x, C)
--output:--
<nothing>
Затем PEP продолжает:
Эти методы предназначены для вызова классов, чей метакласс (получена из) ABCMeta...
Хорошо, попробуйте следующее:
import abc
class MyMeta(abc.ABCMeta): #A metaclass derived from ABCMeta
def __instancecheck__(cls, inst):
print('hello')
return True
class C(metaclass=MyMeta): #A class whose metaclass is derived from ABCMeta
pass
x = C()
C.__instancecheck__(x)
--output:--
hello
Но еще раз isinstance() не вызывает этот метод:
isinstance(x, C)
--output:--
<nothing>
Заключение: PEP 3119 необходимо переписать вместе с документами Data Model.