看书 Python 源码分析笔记 (八) 读源码

前面看了不少章节, 由于 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(), 可能不使用该方法搜索.
也许只在类创建/修改时才使用?

预计有些问题只能在看更多书和代码之后才能解释了.

你可能感兴趣的:(看书 Python 源码分析笔记 (八) 读源码)