scd*_*dmb 11 python python-3.x
有这个代码:
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
Run Code Online (Sandbox Code Playgroud)
为什么__instancecheck__要求[]争论而不是a争论呢?
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;
// ...
Run Code Online (Sandbox Code Playgroud)
不喜欢快车道?你可以尝试这个(风险自负):
>>> 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
Run Code Online (Sandbox Code Playgroud)
我认为 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())
Run Code Online (Sandbox Code Playgroud)
所以根据上面来自 PEP 的引用,你应该能够写
class C:
@classmethod
def __instancecheck__(cls, x):
print('hello')
C.__instancecheck__(C())
--output:--
hello
Run Code Online (Sandbox Code Playgroud)
但是 isinstance() 不会调用该方法:
class C:
@classmethod
def __instancecheck__(cls, y):
print('hello')
x = C()
isinstance(x, C)
--output:--
<nothing>
Run Code Online (Sandbox Code Playgroud)
然后 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
Run Code Online (Sandbox Code Playgroud)
但再次 isinstance() 不会调用该方法:
isinstance(x, C)
--output:--
<nothing>
Run Code Online (Sandbox Code Playgroud)
结论:PEP 3119 需要重写——连同“数据模型”文档。