CPython 实现原理
  • README
  • 一、简介
    • 1.1 如何使用此书
    • 1.2 额外材料和学习资料
  • 二、获取 CPython 源码
    • 2.1 源代码里有什么?
  • 三、准备你的开发环境
    • 3.1 选IDE还是编辑器?
    • 3.2 安装Visual Studio
    • 3.3 安装Visual Studio Code
    • 3.4 安装JetBrains Clion
    • 3.5 安装Vim
    • 3.6 总结
  • 四、编译 CPython
    • 4.1 在 macOS 上编译 CPython
    • 4.2 在 Linux 上编译 CPython
    • 4.3 安装自定义版本
    • 4.4 make 快速入门
    • 4.5 CPython 的 make 目标
    • 4.6 在 Windows 上编译 CPython
    • 4.7 PGO 优化
    • 4.8 总结
  • 五、Python 语言和语法
    • 5.1 为什么 CPython 是用 C 语言而不是用 Python 语言来实现
    • 5.2 Python 语言规范
    • 5.3 分析器生成器
    • 5.4 重新生成语法
    • 5.5 总结
  • 六、配置和输入
    • 6.1 配置状态
    • 6.2 构建配置
    • 6.3 从输入构建模块
    • 6.4 总结
  • 七、基于语法树的词法分析和解析
    • 7.1 具象语法树生成器
    • 7.2 CPython 解析器-分词器
    • 7.3 抽象语法树
    • 7.4 要记住的术语
    • 7.5 一个示例:添加一个约等于比较运算法
    • 7.6 总结
  • 八、编译器
    • 8.1 相关源文件
    • 8.2 重要的专业术语
    • 8.3 实例化一个编译器
    • 8.4 未来标志和编译器标志
    • 8.5 符号表
    • 8.6 核心编译过程
    • 8.7 汇编
    • 8.8 创建一个 Code Object
    • 8.9 使用 Instaviz 展示 Code Object
    • 8.10 一个示例:实现约等于操作符
    • 8.11 总结
  • 九、求值循环
    • 9.1 构建线程状态
    • 9.2 构建帧对象
    • 9.3 帧的执行
    • 9.4 值栈
    • 9.5 例子:在列表中添加元素
    • 9.6 总结
  • 十、内存管理
    • 10.1 C 中的内存分配
    • 10.2 Python 内存管理系统设计
    • 10.3 CPython 内存分配器
  • 十一、并行和并发
    • 11.1 并行和并发模型
    • 11.2 进程的结构
    • 11.3 多进程并行
    • 11.4 多线程
    • 11.5 异步编程
    • 11.6 生成器
    • 11.7 协程
    • 11.8 异步生成器
    • 11.9 子解释器
    • 11.10 总结
  • 十二、对象和类型
    • 12.1 本章的例子
    • 12.2 内置类型
    • 12.3 对象和可变长度对象类型
    • 12.4 类型类
    • 12.5 布尔和整数类型
    • 12.6 Unicode 字符串类型
    • 12.7 字典类型
    • 12.8 总结
  • 十三、标准库
    • 13.1 Python 模块
    • 13.2 Python 和 C 模块
  • 十四、测试套件
    • 14.1 在 Windows 上运行测试套件
    • 14.2 在 Linux 或 MacOS 上运行测试套件
    • 14.3 测试标志
    • 14.4 运行特定测试
    • 14.5 测试模块
    • 14.6 测试工具
    • 14.7 总结
  • 十五、调试
  • 十六、基准测试、性能分析和追踪
  • 十七、下一步计划
    • 17.1 为 CPython 编写 C 扩展
    • 17.2 改进你的 Python 应用程序
    • 17.3 为 CPython 项目做贡献
    • 17.4 继续学习
  • 十八、附录
    • 18.1 C 预处理器
    • 18.2 基础 C 语法
    • 18.3 总结
  • 致谢
Powered by GitBook
On this page
  • 类型槽
  • 在 C 语言中使用类型
  • 类型属性字典
Edit on GitHub
  1. 十二、对象和类型

12.4 类型类

在 Python 中,对象都有 ob_type 属性,你可以使用内置函数 type() 获取这个属性的值:

>>> t = type("hello")
>>> t
<class 'str'>

type() 的返回结果是 PyTypeObject 的一个实例:

>>> type(t)
<class 'type'>

类型对象用于定义抽象基类的实现。

例如,对象总会实现 __repr__() 方法:

>>> class example:
...    x = 1
>>> i = example()
>>> repr(i)
'<__main__.example object at 0x10b418100>'

在任何对象的类型定义中,__repr__() 的实现总是位于相同的地址。这个位置被称之为 类型槽。

类型槽

所有类型槽都定义在 Include/cpython/object.h。

每个类型槽有属性名和函数签名。例如 __repr__() 函数的属性名为 tp_repr,函数签名为 reprfunc。

struct PyTypeObject
---
typedef struct _typeobject {
    ...
    reprfunc tp_repr;
    ...
} PyTypeObject;

函数签名 reprfunc 定义在 Include/cpython/object.h 中,它只有一个参数 PyObject*(self):

typedef PyObject *(*reprfunc)(PyObject *);

例如,cellobject 使用函数 cell_repr 实现了 tp_repr 函数槽:

PyTypeObject PyCell_Type = {
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
    "cell",
    sizeof(PyCellObject),
    0,
    (destructor)cell_dealloc,                   /* tp_dealloc */
    0,                                          /* tp_vectorcall_offset */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
    0,                                          /* tp_as_async */
    (reprfunc)cell_repr,                        /* tp_repr */
    ...
};

PyTypeObject 中除了这些以 tp_ 为前缀的的基本类型槽外,还有其他类型槽定义:

类型槽
前缀

PyNumberMethods

nb_

PySequenceMethods

sq_

PyMappingMethods

mp_

PyAsyncMethods

am_

PyBufferProcs

bf_

每个类型槽都有一个独特的编号,定义在 Include/typeslots.h。当引用或获取一个对象的类型槽时,你应该使用这些常量。

例如,tp_repr 对应的常量为 Py_tp_repr,它的值是 66,其始终与类型槽的位置相匹配。这些常量在检查一个对象是否实现了一个特定的类型槽函数时非常有用。

在 C 语言中使用类型

在 C 扩展模块和 CPython 核心代码中,你会经常使用 PyObject* 类型。

举个例子,如果你在一个可取下标的对象(例如一个列表或字符串)上执行 x[n],则会调用 PyObject_GetItem(),该函数会查看对象 x 以决定如何对其进行取下标操作。

Objects/abstract.c 第 146 行

PyObject *
PyObject_GetItem(PyObject *o, PyObject *key)
{
    PyMappingMethods *m;
    PySequenceMethods *ms;
...

PyObject_GetItem() 可以用于映射类型,例如字典,也可以用于序列类型,例如列表和元组。

如果实例 o 有序列方法,那么 o->ob_type->tp_as_sequence 会被求值为真。如果实例定义了 sq_item 类型槽函数,那么就可以假设它正确实现了序列协议。

检查对象 key 是否可以被转成整数,然后使用 PySequence_GetItem() 从序列对象中取出元素。

ms = o->ob_type->tp_as_sequence;
if (ms && ms->sq_item) {
    if (PyIndex_Check(key)) {
        Py_ssize_t key_value;
        key_value = PyNumber_AsSsize_t(key, PyExc_IndexError);
        if (key_value == -1 && PyErr_Occurred())
            return NULL;
        return PySequence_GetItem(o, key_value);
    }
    else {
        return type_error("sequence index must "
                          "be integer, not '%.200s'", key);
    }
}

类型属性字典

Python 支持使用 class 关键字定义新的类型。用户定义的类型会由类型对象模块中的 type_new() 创建。

用户定义类型有一个属性字典,可以使用 __dict__() 获取。每当在一个自定义类上访问属性时,__getattr__() 的默认实现是在这个属性字典中查找。类方法、实例方法、类属性和实例属性都在这个字典中。

PyObject_GenericGetDict() 实现了获取一个对象字典实例的逻辑。PyObject_GetAttr() 是 __getattr__() 的默认实现,同样 PyObject_SetAttr() 是 __setattr__() 的默认实现。

参见

关于自定义类型有很多层次,这方便有大量的文档。关于 metaclass 就可以写一整本书,但是在本书中,只关注其实现。

Previous12.3 对象和可变长度对象类型Next12.5 布尔和整数类型

Last updated 2 years ago

如果你想学习更多关于元编程的内容,可以参考 Real Python 的 “”。

Python Metaclasses