有这个代码:
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
论据?
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
我认为PEP描述 __instancecheck__()
是错误的。 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需要重写 - 以及“数据模型”文档。