在 Python 3 isinstance() 函数源码解读 中我们讲解了 isinstance()
的源码,同时也看到在 isinstance()
的末尾使用 __instancecheck__()
属性来检查某个字面量或变量是否是某个对象的实例
也就是说
isinstance(3.14,float)
相当于调用了
float.__instancecheck__(3.14)
所以,是否表示很好奇,__instancecheck__()
是什么鬼? 又是怎么实现的
__instancecheck__()
PEP 3119 有几句话
The primary mechanism proposed here is to allow overloading the built-in functions isinstance() and issubclass(). The overloading works as follows: The call isinstance(x, C) first checks whether C.__instancecheck__ exists, and if so, calls C.__instancecheck__(x) instead of its normal implementation.
翻译如下
这里提出的主要机制是允许重载内置函数
isinstance()
和issubclass()
函数。 重载的工作原理如下:调用
isinstance(x,c)
首先检查C.__instancecheck__()
是否存在,如果存在,则调用C.__ instancecheck__(x)
而不是其默认实现
但,这句话其实是错的
比如下面的代码
class C: def do_stuff(self): print('hello') C.do_stuff(C())
运行结果为 hello
如果按照 PEP 中所说,那么下面的代码则可以用来重写 isinstance()
的实现
class C: @classmethod def __instancecheck__(cls, x): print('hello') C.__instancecheck__(C())
不用怀疑,这段代码能正常工作,且输出结果是 hello
但如果你使用了 instance()
来判断,那么结果就是大大的惊叹号了,没被调用
class C: @classmethod def __instancecheck__(cls, y): print('hello') x = C() isinstance(x, C)
输出结果 True
,对,没有任何输出 hello
的倾向
对了,继续看 PEP ,就会看到如下文件
These methods are intended to be be called on classes whose metaclass is (derived from) ABCMeta
翻译如下
这些方法适用于其元类是 ( 派生自 ) 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)
输出结果为 hello
,好吧,看起来正常了
如果我们继续使用 isinstance()
,结果又不是了
isinstance(x, C)
输出结果 True
,对,没有任何输出 hello
的倾向
大家知道为什么吗?
如果你不知道的话,那么可以看看开始提到的那篇文章 Python 3 isinstance() 函数源码解读
原因很简单,就是因为 x.__class__
属性指向的就是 C
>>> x.__class__ <class '__main__.C'>
所以,根本就不会留到调用 __instancecheck__()
方法的时候