前面看了不少章节, 由于 python 很复杂, 有些概念弄不清楚了, 今天复习一下并读一些源码.
回顾 PyTypeObject, PyHeapTypeObject, 这两个结构定义在 object.h 中, 大致可认为 heap-type 是 type 的
子类. Heap-type 将 number-methods 等多个 method 都放入结构中.
这两者的关系还可猜测是 type 是过去为实现内置类型而使用的结构, heap-type 是为实现用户自定义类而使用的
结构. 后期为整合 type,class, 需要将两种 TypeObject 也合并.
存在一个标志 Py_TPFLAGS_HEAPTYPE, 注释说 set if 此类型对象是动态堆上分配的.
在源码中还有 classobject.c, 里面包含 PyClassObject 等三个类, 但是我把它们在 header 中注释掉其创建函数
原型之后也不影响编译(即没有地方创建它们), 也许没有用到? 或者是历史遗留代码?
如类一章所知, PyType_Ready() 函数十分重要, 我们仔细看看其代码, 甚至还调试看看.
根据 pythonrun.c, _Py_ReadyTypes() 的调用相当早期:
void Py_IniitializeEx(...) { ... 命令行读选项 ... // 1. 创建进程, 线程对象. var interp = new PyInterpreterState(); // 当做 Process 对象看. var tstate = new PyThreadState(); // 当做 Thread 对象看. // 2. 初始化类/结构体系 _Py_ReadyTypes() // --> 会调用各内置类型的 PyType_Ready() ... ... 其它初始化, 如 frame,int,modules,builtin, 很多 ... ... }
从初始化代码也几乎透露出 PyType_Ready() 的重要性.
void _Py_ReadyTypes() { PyType_Ready(&PyType_Type); // 先准备此 meta-type 接着是 weakref, bool, str, list, none 等类型 ... }
下面较完整的看下 PyType_Ready() 部分伪代码:
// 返回 < 0 表示失败. int PyType_Ready(PyTypeObject *type) { var base = type->base; // 为方便, 假定后面给 base 赋值也即给 type->base 赋值. // 以下错误处理也都略去. if (type->flags & READY) return 0; // 已经就绪了就立刻返回. type->flags |= READYING; // 设置标志: 正在准备中..., 避免递归进入. // 1. 如果未设置 type 的基类, 则缺省基类为内部的 base-object-type. if (base == null && type is-not base-object-type) base = base-object-type; // 2. 如果基类未就绪, 则先准备基类. if (base != null && base not& READY) PyType_Ready(base); // 递归准备基类. // 3. 如果未给出 type, 则缺省使用 base 的 type. // 对于 python 基本类型, type->type 一般是 base-object-type. if (type->type == null && base) type->type = base->type; // 4. 初始化 tp_bases (基类 tuple) Tuple bases = type->tp_bases = new Tuple(...); // 5. 初始化 dict Dict dict = type->tp_dict = new Dict(); // 6. 添加此类型的 descriptors (各种函数,方法,成员等描述符) add_operators(type); add_methods(type) 如果需要的话; add_members(type) ...; add_getset(type) ...; // 7. 计算 mro -- method resolution order mro_internal(type); // 8. ?? Inherit special flags from dominant(主要的,显性的) base inherit_special(type, type->base); // 9. 根据 mro 初始化 tp_dict Tuple bases = type->tp_mro; for (int i = 0; i < length(bases); ++i) // 遍历所有基类 inherit_slots(type, bases[i]); // 从每基类继承内容 // Sanity check for tp_free. ... // 设置 __doc__ 属性(略) // 10. Some more special stuff(继承...) type->tp_as_number = base->... if is null; 同样对 as_seq, as_map, as_buf // 11. 向每个父类添加子类(自己) Tuple bases = type->tp_bases; for (int i = 0; i < length(bases); ++i) add_subclass(bases[i], type); // 12. DONE, 设置标志. type->flags = ...; }
当前可以把它看做是填写 type 的对应虚表的过程.
看代码, 不仅有 add_subclass(base, type), 还有 remove_subclass(). 后者在 type_set_bases() 中用于更新
类信息. 这样我们知道(猜测?) python 可以动态改变一个类的基类, 这是一种强大的功能!
定义 __bases__ 可被读写是在 PyGetSetDef type_getsets[] 数组中, 为 __bases__ 提供了 getter, setter 函数.
同样对 name,module 提供了 getter/setter, 而 dict,doc 只提供了 getter, 从而成为只读属性.
函数 mro_implementation() 似乎通过合并 list 的方法, 构造出 type 所有基类的 mro 的合并 tuple 作为 mro.
细节我想先了解一下 MRO 算法 C3 也许再看代码会更好一点.
函数 _PyType_Lookup(type, string name) 用于实现在此类及所有基类中查找 name 槽中数据:
PyObject *_PyType_Lookup(PyTypeObject *type, PyObject *name) { tuple mro = type->tp_mro; for (int i = 0; i < length(mro); ++i) { base = mro[i]; if (base is PyClassObject) ...; // 历史遗留代码?? else // 必须是 PyTypeObject item = base->dict[name]; if (item) return item; // 如果找到则返回. } return null; // 所有基类都未找到, 则返回 null }
这里使用了 mro 序进行搜索. 似乎在访问 type 的属性/方法时, 会使用此顺序搜索. 暂时不知道访问其它类的实例
时, 会不会进行 mro 搜索. 在一个多层继承体系中搜索多个 dict, 会比较影响性能.
实验一下, 用下面的例子检查当调用 c.foo() 时, 是否进行 mro 搜索:
# 定义两个简单基类 class A: def foo(self): print ("foo") class B: def bar(self): print ("bar") # 类 C 继承自 A,B (多继承) class C(A,B): pass c = C() # 创建一个 C 的实例. c.foo() or c.bar() # 会执行 mro 搜索吗?
据我试验, 调用 c.foo() 时断点未进入 _PyType_Lookup(), 可能不使用该方法搜索.
也许只在类创建/修改时才使用?
预计有些问题只能在看更多书和代码之后才能解释了.