isinstance(obj,class_or_tuple)
函数返回一个对象 obj
是否是某个类的实例或者是某个类的子类
参数 class_or_tuple
是可以是多个类组成的元素,例如 isinstance(x,)A,B,...))
。如果是一个原则,则分别检查,相当于
isinstance(x,A) or isinstance(x,B) or ...
如果传递的 obj
是一个字面量,则用于检查是否是某个类的实例,否则检查是否是某个类的子类
isinstance()
不知道你是否看过了 Python 3+ 一切都是对象 这篇文章,不管怎么样,你一定会对以下的结果好奇
>>> isinstance(3,object) True >>> isinstance(3.14, object) True >>> isinstance(max,object) True
也就是说,看起来,所有的对象都是 object
的实例
>>> isinstance(3,type) False >>> isinstance(3.14, type) False >>> isinstance(max,type) False
但看起来所有的字面量又不是 type
的实例
如果你再输入以下代码
>>> isinstance(object,type) True >>> isinstance(int,type) True >>> isinstance(float,type) True
是不是要疯掉,因为字面量的类型又是 type
的实例,其实,这种说法是不正确的,应该说这些字面量的类型是 type
类型的子类
这,到底是怎么回事呢?
所有的好奇,都指向了 isinstance()
是如何实现的
我们来翻翻源码,还是 Python
目录下的文件 bltinmodule.c
文件
Python/bltinmodule.c
static PyObject * builtin_isinstance_impl(PyObject *module, PyObject *obj, PyObject *class_or_tuple) { int retval; retval = PyObject_IsInstance(obj, class_or_tuple); if (retval < 0) return NULL; return PyBool_FromLong(retval); }
你可能很好奇,为什么多了一个参数 module
,这个参数是用来表示该函数属于哪个模块的,比如内建函数属于 builtins
>>> isinstance.__module__ 'builtins'
先调用 PyObject_IsInstance
判断类型,然后再调用 PyBool_FromLong
格式化结果
既然 retval
能够和 0
做比较,那么 PyObject_IsInstance
的返回值一定是数值类型
所以,为了方便起见,我们先看 PyBool_FromLong
的实现
PyObject *PyBool_FromLong(long ok) { PyObject *result; if (ok) result = Py_True; else result = Py_False; Py_INCREF(result); return result; }
也就是根据传入的值,转换为 True
或 False
PyObject_IsInstance()
好了,那继续回到 PyObject_IsInstance()
int PyObject_IsInstance(PyObject *inst, PyObject *cls) { _Py_IDENTIFIER(__instancecheck__); PyObject *checker; /* Quick test for an exact match */ if (Py_TYPE(inst) == (PyTypeObject *)cls) return 1; /* We know what type's __instancecheck__ does. */ if (PyType_CheckExact(cls)) { return recursive_isinstance(inst, cls); } if (PyTuple_Check(cls)) { Py_ssize_t i; Py_ssize_t n; int r = 0; if (Py_EnterRecursiveCall(" in __instancecheck__")) return -1; n = PyTuple_GET_SIZE(cls); for (i = 0; i < n; ++i) { PyObject *item = PyTuple_GET_ITEM(cls, i); r = PyObject_IsInstance(inst, item); if (r != 0) /* either found it, or got an error */ break; } Py_LeaveRecursiveCall(); return r; } checker = _PyObject_LookupSpecial(cls, &PyId___instancecheck__); if (checker != NULL) { PyObject *res; int ok = -1; if (Py_EnterRecursiveCall(" in __instancecheck__")) { Py_DECREF(checker); return ok; } res = PyObject_CallFunctionObjArgs(checker, inst, NULL); Py_LeaveRecursiveCall(); Py_DECREF(checker); if (res != NULL) { ok = PyObject_IsTrue(res); Py_DECREF(res); } return ok; } else if (PyErr_Occurred()) return -1; /* Probably never reached anymore. */ return recursive_isinstance(inst, cls); }
_Py_IDENTIFIER
_Py_IDENTIFIER(__instancecheck__)
跟 _Py_IDENTIFIER
相关的代码如下
Include/object.h
typedef struct _Py_Identifier { struct _Py_Identifier *next; const char* string; PyObject *object; } _Py_Identifier; #define _Py_static_string_init(value) { .next = NULL, .string = value, .object = NULL } #define _Py_static_string(varname, value) static _Py_Identifier varname = _Py_static_string_init(value) #define _Py_IDENTIFIER(varname) _Py_static_string(PyId_##varname, #varname)
他们的作用是管理静态字符串,基本的使用方式如下
假设存在一个函数调用
r = PyObject_CallMethod(o, "foo", "args", ...);
那么使用 _Py_IDENTIFIER
就可以写成下面这样啦
_Py_IDENTIFIER(foo); ... r = _PyObject_CallMethodId(o, &PyId_foo, "args", ...);
也就是 _Py_IDENTIFIER(foo);
会自动创建一个静态的字符串变量 PyId_foo
,可以是块级别的,也可以是文件级别的
C++ 编译器在预处理的时候,会将字符串 foo
预处理成 PyId_foo
并连接到指定结构
当预处理结束,所有的字符串都通过 _PyUnicode_ClearStaticStrings
被释放
所以 _Py_IDENTIFIER(__instancecheck__)
相当于创建了一个名为 PyId__instancecheck__
的块级的静态变量
从后面的代码也可以看出,的确如此
Py_TYPE
if (Py_TYPE(inst) == (PyTypeObject *)cls) return 1;
用于检查 inst
中的 ob_type
的值是否就是 cls
,如果是则直接返回 1
Py_TYPE()
的实现也在 object.h
中
#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
PyType_CheckExact
if (PyType_CheckExact(cls)) { return recursive_isinstance(inst, cls); }
这段代码用于检查 cls
是否就是 type
类型,如果是的话则递归检查 inst
的继承链
什么意思呢? 其实就是除了 type
类型的类型是 type
外,所有用户自定义的类型的 type()
结果都是 type
不信的话,你可以自己测试下下面代码
>>> class Foo: ... pass ... >>> Foo.__class__ <class 'type'> >>> class Foo(int): ... pass ... >>> type(Foo) <class 'type'>
recursive_isinstance
recursive_isinstance()
函数的源代码在 Objects/abstract.c
中
static int recursive_isinstance(PyObject *inst, PyObject *cls) { PyObject *icls; int retval = 0; _Py_IDENTIFIER(__class__); if (PyType_Check(cls)) { retval = PyObject_TypeCheck(inst, (PyTypeObject *)cls); if (retval == 0) { PyObject *c = _PyObject_GetAttrId(inst, &PyId___class__); if (c == NULL) { if (PyErr_ExceptionMatches(PyExc_AttributeError)) PyErr_Clear(); else retval = -1; } else { if (c != (PyObject *)(inst->ob_type) && PyType_Check(c)) retval = PyType_IsSubtype( (PyTypeObject *)c, (PyTypeObject *)cls); Py_DECREF(c); } } } else { if (!check_class(cls, "isinstance() arg 2 must be a type or tuple of types")) return -1; icls = _PyObject_GetAttrId(inst, &PyId___class__); if (icls == NULL) { if (PyErr_ExceptionMatches(PyExc_AttributeError)) PyErr_Clear(); else retval = -1; } else { retval = abstract_issubclass(icls, cls); Py_DECREF(icls); } } return retval; }
这个函数很简单,以 PyType_Check(cls)
作为分水岭,检查 cls
的 __class__
属性是否为 <class 'type'>
- 如果是的话,使用下面的代码检查
inst
的ob_type
是否就是cls
,或者cls
是否就是inst
的父类之一retval = PyObject_TypeCheck(inst, (PyTypeObject *)cls);
其中 PyObject_TypeCheck
的源码如下
#define PyObject_TypeCheck(ob, tp) \ (Py_TYPE(ob) == (tp) || PyType_IsSubtype(Py_TYPE(ob), (tp)))
如果是则直接返回检查的结果 retval
如果不是,且有没有定义 __class__
属性则抛出异常
如果不是,但定义了 __class__
属性,那么就检查 __class__
的类是否是 cls
或 cls
的子类
- 如果不是的话,则直接检查
__class__
属性,再根据是否存在__class__
属性来检查
recursive_isinstance()
函数解析就到此为止吧,源码很简单,一看九洞
### 如果 cls 参数是元组
继续下面的代码
```c if (PyTuple_Check(cls)) { Py_ssize_t i; Py_ssize_t n; int r = 0;
if (Py_EnterRecursiveCall(" in __instancecheck__")) return -1; n = PyTuple_GET_SIZE(cls); for (i = 0; i < n; ++i) { PyObject *item = PyTuple_GET_ITEM(cls, i); r = PyObject_IsInstance(inst, item); if (r != 0) /* either found it, or got an error */ break; } Py_LeaveRecursiveCall(); return r; }
```
检查参数 class_or_tuple
是否是一个元组,如果是元组则迭代检查元组中某个元素是否就是 inst
的类型或 inst
的父类的类型
最后,检查 inst
是否是 cls
的实例
如果上面的检查都得不到结果,那么只能检查 inst
的 __instancecheck__
属性了
这个属性也非常有意思
>>> class Foo(int): ... pass ... >>> type(Foo) <class 'type'> >>> Foo.__instancecheck__ <built-in method __instancecheck__ of type object at 0x7f9bfe703148> >>> int.__instancecheck__ <built-in method __instancecheck__ of type object at 0x108a72d70> >>> float.__instancecheck__ <built-in method __instancecheck__ of type object at 0x108a6f8b0>
下面一系列的代码
>>> float.__instancecheck__(3.14) True >>> float.__instancecheck__(3) False >>> type.__instancecheck__(3.14) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: descriptor '__instancecheck__' requires a 'type' object but received a 'float' >>> object.__instancecheck__(3) True
算了,这章节就到此结束吧,下一章节我们来详细介绍下 __instancecheck__
这个属性