有点意思的 Python 系列二 内置类型增加额外方法(二)
有点意思的 Python 系列二 内置类型增加额外方法 中介绍了一种通过 Python 代码实现对内置的类型增加自定义的方法。
今天再介绍一种方法,实现更为底层。那就是直接修改 cPython
的源码。
这里演示给 list
和 dict
增加 deepcopy
和 tojson
方法,实现对 list
和 dict
的深拷贝和把一个 list
和 dict
转换为 json
的方法。
给 int
和 float
增加 add
, sub
, mul
, div
方法,实现加减乘除。
Step 1 下载代码
cPython
代码可以在 GitHub 上找到: cPython
配置环境
我使用的是 macOS 系统,所以这里就只介绍 macOS 需要的环境
- 安装 C 编译器和工具包
xcode-select --install
- 安装其他工具
brew install openssl@3 xz zlib gdbm sqlite
Step 2 修改代码
首先将分支切换到 3.12,不使用 main
的原因是 main
分支随时都在更新,所以选择旧版本的分支,确保代码的一致性。
确保 3.12 分支的代码 commit sha
为 0181aa2e3efedc6504b27f6fe74f096e5e454286
修改 list
list
的实现在 ./Objects/listobject.c
中。我们的修改就在这里。
首先找到 static PyMethodDef list_methods[]
这里包括了 list
相关的方法。在这一行的上面,增加以下代码
static PyObject * |
其中 list_method_deepcopy
就是 deepcopy
的具体实现,list_method_tojson
是 tojson
的具体实现。
然后将上面两个方法增加到 list_methods
中
注意,要增加到最后一行 {NULL, NULL} /* sentinel */
上方
{"deepcopy", (PyCFunction)list_method_deepcopy, METH_NOARGS, PyDoc_STR("Return a deep copy of the list")}, |
最后完整的 list_methods[]
static PyMethodDef list_methods[] = { |
修改 dict
和修改 list 类似,dict 的实现在 ./Objects/dictobject.c
中。
同理还是首先找到 static PyMethodDef mapp_methods[]
这里包括了 dict
相关的方法。在这一行的上面,增加以下代码。
static PyObject * |
可以看到,这里的代码除了方法名字,实现方法和 list
的一致,当然。最后也还是要加到 mapp_methods[]
中
{"deepcopy", (PyCFunction)dict_method_deepcopy, METH_NOARGS, PyDoc_STR("Return a deep copy of the dict")}, |
完整的 mapp_methods
static PyMethodDef mapp_methods[] = { |
修改 float
float 的实现在 ./Objects/floatobject.c
中。 同理找到 float_methods[]
在上方增加以下方法
static PyObject * |
类似的在 float_methods[]
中增加
{"add", (PyCFunction)float_method_add, METH_VARARGS, PyDoc_STR("Add float or int objects")}, |
修改 int
int
的实现是在 .Objects/longobject.c
中。找到 long_methods[]
在上方增加方法
static PyObject * |
这里的方法实现和 int
中的实现有细微差别。最后还是一样增加 long_methods[]
{"add", (PyCFunction)long_method_add, METH_VARARGS, PyDoc_STR("Add int or float objects")}, |
Step 3 编译
使用以下命令编译代码
LDFLAGS="-L$(brew --prefix zlib)/lib -L$(brew --prefix bzip2)/lib -L$(brew --prefix openssl@3)/lib" \ |
编译成功后在目录下会有一个 python.exe
(为什么在 macOS 是 .exe 可参考 build-instructions)
Step 4 测试
新建一个 .py 文件
origin_dict = {"goods": ["apple", "orange"]} |
输出结果如下
================================================== |
由此完成了从 cPython 的改造实现了以上几种方法。
补充
- 直接修改 cPython 的源码实现好处是实现了原生实现。但是缺点也很明显。就是这样写的代码,只能运行在这个修改后的 Python 环境,否则就会报错。
但是换个思路想,这样也会带来一个好处就是间接的保护了代码。这个具体的内容后面可以在展开聊聊。 - 这里编译的 Python 其实只是一个调试环境,因为没有启用 PGO 所以编译很快,但是不能用于生产环境。
如果想把这个版本的 Python 用在生产环境,可以在./configure
的时候增加--enable-optimizations
参数。具体内容可参考 profile-guided-optimization