有这段代码:
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
参数调用?{/ p>
答案 0 :(得分:8)
PyObject_IsInstance
会对完全匹配进行快速测试。
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
答案 1 :(得分:4)
我认为描述__instancecheck__()
的PEP是错误的。 PEP 3119说:
这里提出的主要机制是允许重载 内置函数isinstance()和issubclass()。超载 工作原理如下:调用isinstance(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 - 以及“数据模型”文档。