我正在将纯Python模块转换为C扩展,以熟悉C API。
Python实现如下:
_CRC_TABLE_ = [0] * 256
def initialize_crc_table():
if _CRC_TABLE_[1] != 0: # Safeguard against re-initialization
return
# snip
def calculate_crc(data: bytes, initial: int = 0) -> int:
if _CRC_TABLE_[1] == 0: # In case user forgets to initialize first
initialize_crc_table()
# snip
# additional non-CRC methods trimmed
到目前为止,我的C扩展名有效:
#include <Python.h>
static Py_ssize_t CRC_TABLE_LEN = 256;
PyObject *_CRC_TABLE_;
static PyObject *method_initialize_crc_table(PyObject *self, PyObject *args) {
// snip
}
static PyMethodDef module_methods[] = {
{"initialize_crc_table", method_initialize_crc_table, METH_VARARGS, NULL},
{NULL, NULL, 0, NULL}
};
void _allocate_table_() {
_CRC_TABLE = PyList_New(CRC_TABLE_LEN);
PyObject *zero = Py_BuildValue("i", 0);
for (int i = 0; i < CRC_TABLE_LEN; i++) {
PyList_SetItem(_CRC_TABLE_, i, zero);
}
}
#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef module_utilities = {
PyModuleDef_HEAD_INIT,
"utilities",
NULL,
-1,
module_methods,
};
PyMODINIT_FUNC PyInit_utilities() {
PyObject *module = PyModule_Create(&module_utilities);
_allocate_table_();
PyModule_AddObject(module, "_CRC_TABLE", _CRC_TABLE_);
return module;
}
#else
PyMODINIT_FUNC initutilities() {
PyObject *module = Py_InitModule3("utilities", module_methods, NULL);
_allocate_table_();
PyModule_AddObject(module, "_CRC_TABLE", _CRC_TABLE_);
}
我能够从解释器的C扩展名访问utilities._CRC_TABLE_
,并且在调用utilities.intialize_crc_table
时值与Python等效。
现在,我尝试在initialize_crc_table
的开头调用calculate_crc
,执行与Python实现中相同的检查。我现在返回None
:
static PyObject *method_calculate_crc(PyObject *self, PyObject *args) { if (!(uint)PyLong_AsUnsignedLong(PyList_GetItem(_CRC_TABLE_, (Py_ssize_t) 1))) { PyObject *call_initialize_crc_table = PyObject_GetAttrString(self, "initialize_crc_table"); PyObject_CallObject(call_initialize_crc_table, NULL); Py_DECREF(call_initialize_crc_table); } Py_RETURN_NONE; }
我已将此添加到
module_methods[]
,并且它编译时没有警告或错误。当我在解释器中运行此方法时,我遇到了段错误。我认为这是因为self
不是作为对象的模块。
我可以这样做,这似乎没有问题:
static PyObject *method_calculate_crc(PyObject *self, PyObject *args) { if (!(uint)PyLong_AsUnsignedLong(PyList_GetItem(_CRC_TABLE_, (Py_ssize_t) 1))) { method_initialize_crc_table(self, NULL); } Py_RETURN_NONE; }
但是,我不确定是否应该将
self
,NULL
或其他内容传递给该方法。
从method_initialize_crc_table
调用method_calculate_crc
的正确方法是什么?
我正在将纯Python模块转换为C扩展名,以熟悉C API。 Python实现如下:_CRC_TABLE_ = [0] * 256 def initialize_crc_table():if ...
这里有一个“陷阱”,我必须澄清。虽然代码是为Python 3设计的,但是开发最初是在Python 2中完成的,因为开发文件在我使用的机器上尚不可用。这揭示了每个版本在处理方式上的一些差异。大卫的评论有助于澄清。