以下为个人学习笔记整理
# 编写 Python 的 C 扩展
# 构建编译基础环境
- 启动 visual studio installer
- 安装相关插件和依赖库
# 编写 C 扩展代码
简单的 C 扩展大致结构图:
- 定义函数: PyMethodDef
- 定义模块: PyModuleDef
- 定义初始化模块函数:PyInit_module
# 定义函数
函数名:
模块名_函数名
参数类型
ml_flags
:- METH_VARARGS :只包含元组参数
def func(self, *args)
- METH_VARARGS | METH_KEYWORDS :包含元组及字典参数
def func(self, *args, **kwargs)
- METH_FASTCALL :固定个数的元组参数。顾名思义,采用快速调用效率较高。
def func(self, arg1, arg2, argN)
- METH_FASTCALL | METH_KEYWORDS :固定个数元组参数 + 字典参数。
def func(self, arg1, arg2, argN, **kwargs)
- METH_NOARGS :无参函数。
def func(self)
- METH_O :单个参数。
def func(self, arg1)
- METH_CLASS :类函数。
def func(cls)
- METH_STATIC :静态函数。
def func()
- METH_VARARGS :只包含元组参数
逻辑代码
参数解析(PyArg_Parse):
PyArg_ParseTuple :解析位置参数。
PyArg_ParseTuple(PyObject* args, const char* format, ...)
int i,j;
char *s;
PyArg_ParseTuple(args,"iis", &i, &j, &s)
// Python call:func(i:int, j:int, s:str)
PyArg_VaParse:用于解析可变数量参数,通过
var_list
承载。PyArg_VaParse(PyObject* args, const char* format, va_list vargs)
。- 相关使用案例
PyArg_ParseTupleAndKeywords:解析位置和关键字参数。
PyArg_ParseTupleAndKeywords(PyObject* args, PyObject* kw, const char* format, char* keywords[], ...)
char *a;
char *b;
char *foo = NULL;
char *bar = NULL;
char *baz = NULL;
static char *keywords[] = {"a","b","foo","bar","baz", NULL};
PyArg_ParseTupleAndKeywords(args, kwargs,"ss|sss", keywords, &a, &b, &foo, &bar, &baz)
// Python call:
// func() // Fails, require a and b
// func('a') // fails, requires b
// func('a', 'b')
// func('a', 'b', foo='foo', bar='bar', baz='baz)
// func('a', 'b','foo', 'bar', 'baz')
// func(a='a', b='b', foo='foo', bar='bar', baz='baz')
PyArg_VaParseTupleAndKeywords :解析可变数量参数和关键字参数。
PyArg_VaParseTupleAndKeywords(PyObject* args, PyObject* kw, const char* format, char* keywords[], va_list vargs)
PyArg_ValidateKeywordArguments :解析关键字参数,关键字必须为字符串
PyArg_ValidateKeywordArguments(PyObject* kw)
PyArg_UnpackTuple :解析特定长度范围的元组。
PyArg_UnpackTuple(PyObject* args, const char* name, Py_ssize_t* min, Py_ssize_t* max, ...)
返回值 ——
Py_BuildValue
:PyObject *Py_BuildValue(const char *format, ...)
Py_BuildValue("") None
Py_BuildValue("i", 123) 123
Py_BuildValue("iii", 123, 456, 789) (123, 456, 789)
Py_BuildValue("s", "hello") 'hello'
Py_BuildValue("y", "hello") b'hello'
Py_BuildValue("ss", "hello", "world") ('hello', 'world')
Py_BuildValue("s#", "hello", 4) 'hell'
Py_BuildValue("y#", "hello", 4) b'hell'
Py_BuildValue("()") ()
Py_BuildValue("(i)", 123) (123,)
Py_BuildValue("(ii)", 123, 456) (123, 456)
Py_BuildValue("(i,i)", 123, 456) (123, 456)
Py_BuildValue("[i,i]", 123, 456) [123, 456]
Py_BuildValue("{s:i,s:i}", "abc", 123, "def", 456) {'abc': 123, 'def': 456}
Py_BuildValue("((ii)(ii)) (ii)", 1, 2, 3, 4, 5, 6) (((1, 2), (3, 4)), (5, 6))
format
格式:
字符 | 类型 | 含义 |
---|---|---|
s | const char * | 字符串 |
s* | Py_buffer | 生成的 C 字符串可能包含嵌入的 NULL 字节 |
s# | const char *,int | 拆解出两个对象,一个 str,一个 len |
z | const char * | 字符串或者 None |
z* | Py_buffer | 生成的 C 字符串可能包含嵌入的 NULL 字节或者 None |
z# | const char *,int | 拆解出两个对象,一个 str 或者 None,一个 len |
y | const char * | 只读字节对象 |
S | PyBytesObject * | python 的 bytes 对象 |
Y | PyByteArrayObject * | Python 的 bytearray 对象 |
b | int | 无符号整数 |
B | int | 无符号整数且不进行溢出检查 |
h | int | 短整型 |
H | int | 不进行溢出检查的短整型 |
i | int | 正常整数 |
l | long int | C 的长整形 |
c | char | 字符 |
d | double | 双精度浮点 |
f | float | 单精度浮点 |
p | bool | bool 类型本质上还是 int |
D | Py_complex * | 复数 |
(...) | tuple | 序列 |
[...] | list | 列表 |
{...} | dict | 字典 |
O | PyObject* | Python 对象,不修改其引用计数 |
O& | convert+void* | 将 Python 对象转为 C。 C-obj = converter(py-obj, O&); |
\| | 表示后续参数是可选的 i|s : 一个整数,一个字符串。字符串可选 | |
$ | 表示后续参数仅是关键字 | |
: | 格式单位列表到此结束;在错误消息中使用冒号后的字符串作为函数名 | |
; | 格式单位列表到此结束;分号后的字符串用作错误消息。 : 和 ; 相互排斥。 |
# exmaple:
# 定义 Python 的 C 扩展
[ccore.c] | |
# include <Python.h> | |
static PyObject* ccore_calc(PyObject* self){ | |
return Py_BuildValue("i",100); | |
} | |
struct PyMethodDef module_methods[] = { | |
{"calc",(PyCFunction)ccore_calc, METH_NOARGS, "这是一个简单测测试"}, | |
{NULL, NULL, 0, NULL} | |
}; | |
static struct PyModuleDef modulemethod = { | |
PyModuleDef_HEAD_INIT, | |
"ccore", /* name of module */ | |
"A sample module", /* Doc string (may be NULL) */ | |
-1, /* Size of per-interpreter state or -1 */ | |
module_methods /* Method table */ | |
}; | |
PyMODINIT_FUNC PyInit_ccore(void) | |
{ | |
return PyModule_Create(&modulemethod); | |
} |
[setup.py] | |
from distutils.core import setup | |
from distutils.extension import Extension | |
setup(name='Py_ccore', | |
version='1.0', | |
ext_modules=[Extension('ccore', ['ccore.c'])], | |
) |
# 构建指令
python setup.py install |
# 调用 Python 的 C 扩展
import ccore | |
print(ccore.calc()) | |
Out:100 |
# 定义 PyMethodDef
- C 层的定义
struct PyMethodDef { | |
char *ml_name; | |
PyCFunction ml_meth; | |
int ml_flags; | |
char *ml_doc; | |
}; |
字段 | 类型 | 含义 |
---|---|---|
ml_name | const char * | 函数名 |
ml_meth | PyCFunction | 函数指针 |
ml_flags | int | 参数类型 |
ml_doc | const char * | 函数说明 |
# C 层的使用:
struct PyMethodDef module_methods[] = { | |
{"calc",(PyCFunction)ccore_calc, METH_NOARGS, "func.__doc__"}, | |
# .... | |
{NULL, NULL, 0, NULL} | |
}; |
等价于定义了一个如下结构的函数,不包含实现和返回值
def calc(): | |
"""func.__doc__"""" | |
... |
# 定义 PyModuleDef
- C 层的定义
struct PyModuleDef { | |
PyModuleDef_HEAD_INIT, | |
char *m_name; | |
char *m_doc; | |
Py_ssize_t m_size; //。 | |
PyMethodDef * m_methods; | |
} |
字段 | 类型 | 含义 |
---|---|---|
m_base | PyModuleDef_Base | 始终将此成员初始化为 PyModuleDef_HEAD_INIT |
m_name | const char * | 模块名 |
m_doc | const char * | 模块说明 |
m_size | Py_ssize_t | 设置存储模块, -1 表示全局状态,不支持子解释器。 更多详细信息,请参见 PEP 3121 |
m_methods | PyMethodDef * | 函数列表指针 |
# C 层的使用:
static struct PyModuleDef modulemethod = { | |
PyModuleDef_HEAD_INIT, | |
"ccore", | |
"A sample module", | |
-1, | |
module_methods | |
}; |
等价于定义了一个如下结构的模块
[ccore.py] | |
"""A sample module""" | |
def calc(): | |
"""func.__doc__"""" | |
... | |
... |
# 定义初始化模块函数
如下代码定义模块的创建
PyMODINIT_FUNC PyInit_ccore(void) // PyInit_模块名 | |
{ | |
return PyModule_Create(&modulemethod); // modulemethod is PyModuleDef obj | |
} |
参考资料:
Python 官方手册