近期笔者在研究python内部部分模块的实现机理,研究着研究着就开始硬刚C源码了。想着先前工作或是日常也没有体验过用C++编写python库,于是就刚好学习了一下。
用C或者C++编写python的扩展库,建议用Visual Studio宇宙第一IDE,一来能够同时支持python跟C,二来调试功能非常强大。入门上手的话,可以参考下面的文档:
- 官网文档:扩展和嵌入 Python 解释器
- VS文档:Create a C++ extension for Python
编写的库叫做cplayground
,只包含一个hack函数tuple_setitem
——强行设置tuple的元素(python默认是不支持的)。我们可以来看这样的python扩展用C++该如何实现:
首先参考上面的VS文档Create a C++ extension for Python
,部署基础环境,一个Solution里需要包含用于测试的Python Project以及用来编写扩展的C++ Project。环境部署有几个要点需要注意:
- 由于是编写扩展,需要编译为dll,文件后缀名为.pyd
- 确认下libs有没有debug库,没有的话Preprocessor跟Code Generation的设置都不能带debug
- python项目的Debug设置里,解释器参数加上
-i
,且启用native code debugging,这样才能在python跟c代码打断点调试 - 可以写一个最简的框架先尝试build成功,并且能顺利装到对应python的packages里,跑通整个流程先
setup.py
中注意模块名字要全部对应上
值得一提的是,如果VS没有预装python发行版本,VS自带的python环境管理模块也能检测到你以前另外安装的python,所以不用担心python环境方面的问题。
搭好整个框架流程之后,我们可以实现tuple_setitem
具体的逻辑了。整个cpp代码如下:
1 | // cplayground.cpp |
在模块的cpp实现中,顶头必须要#include <Python.h>
,之后我们可以把自己需要暴露出去的函数给实现了(这里可以看到模块的主cpp文件能够起到胶水层的作用,如果此时有其它头文件里定义了一系列接口,就可以在这个文件里把这些接口适配为python可识别的函数模式)。实现完成之后,通过method def
跟module def
定义接口列表跟模块,然后再在下面定义模块启动函数,一个python的C扩展就诞生了。
那么怎么实现tuple_setitem
的逻辑呢?我们需要预想下python端如何调用:
1 | import cplayground |
在C层的实现上,tuple_setitem
的签名是两个参数:module自己的引用、打包的参数集合,都是PyObject*
。
首先第一步是通过PyArg_ParseTuple
看是否能用对应的模式解包参数。关于参数的模式,可以参考这个文档。
之后需要对解包的参数再检查,比如检查解出来的PyObject* tuple
是否真正是tuple object
,以及计算索引是不是有溢出的情况。
最后通过Py_SETREF
,就能把传进来的元组的内部元素重置。整个实现过程,其实是参考了list object
的相关实现,有兴趣的同学可以深入探索一下(预告:在后面的文章里也会提到这个)。
整个小项目都放到了github上,是一个sln。如果要测试的话,clone之后需要注意重新走一遍环境配置的过程,确认配置无误。