我正在使用numpy的C API编写一些用于矩阵计算的函数。今天,我想将函数的某些部分移到单独的.c文件中,并使用标头对其进行声明。现在,我有一个奇怪的问题,与numpyimport_array函数有关。我试图尽可能简化问题。首先有一个工作程序:
import_array
mytest.c
#include "mytest.h" PyObject* my_sub_function() { npy_intp dims[2] = {2, 2}; double data[] = {0.1, 0.2, 0.3, 0.4}; PyArrayObject* matrix = (PyArrayObject*)PyArray_SimpleNew(2, dims, NPY_FLOAT64); memcpy(PyArray_DATA(matrix), data, sizeof(double) * dims[0] * dims[1]); return (PyObject*)matrix; } static PyObject* my_test_function(PyObject* self, PyObject* args) { return my_sub_function(); } static PyMethodDef methods[] = { {"my_test_function", my_test_function, METH_VARARGS, ""}, {0, 0, 0, 0} }; static struct PyModuleDef module = { PyModuleDef_HEAD_INIT, "mytest", 0, -1, methods }; PyMODINIT_FUNC PyInit_mytest() { import_array(); return PyModule_Create(&module); }
mytest.h
#ifndef mytest_h #define mytest_h #include <Python.h> #include <numpy/arrayobject.h> #include <numpy/npy_common.h> PyObject* my_sub_function(); #endif
生成文件
all: mytest.o sub.o gcc -shared -Wl,-soname,mytest.so -o mytest.so mytest.o mytest.o: sub.o gcc -fPIC -c mytest.c `pkg-config --cflags python3` clean: rm -rf *.so rm -rf *.o
一切正常。我可以调用make然后加载模块并调用函数:
make
test.py
import mytest print(mytest.my_test_function())
如果我import_array要从init函数中删除,则会出现段错误,这是许多邮件列表和论坛中已报告的行为。
现在,我只想my_sub_function从 mytest.c中 删除整个函数,然后将其移动到名为 sub.c 的文件中:
my_sub_function
#include "mytest.h" PyObject* my_sub_function() { npy_intp dims[2] = {2, 2}; double data[] = {0.1, 0.2, 0.3, 0.4}; PyArrayObject* matrix = (PyArrayObject*)PyArray_SimpleNew(2, dims, NPY_FLOAT64); memcpy(PyArray_DATA(matrix), data, sizeof(double) * dims[0] * dims[1]); return (PyObject*)matrix; }
新的 Makefile 是:
all: mytest.o sub.o gcc -shared -Wl,-soname,mytest.so -o mytest.so mytest.o sub.o mytest.o: gcc -fPIC -c mytest.c `pkg-config --cflags python3` sub.o: gcc -fPIC -c sub.c `pkg-config --cflags python3` clean: rm -rf *.so rm -rf *.o
如果我尝试加载模块并立即调用函数,则函数调用会给我一个段错误。如果将调用放在import_array的顶部,则可以解决问题my_sub_function,但我认为这不是应使用该函数的方式。
因此,我想知道为什么会这样,以及将numpy模块拆分为几个源文件的“干净”方法是什么。
默认情况下,该import_array例程将仅在单个文件中提供NumPy C API。这是因为它通过存储在静态全局变量(即,未导出,仅在同一文件中可见)的函数指针表起作用。
如文档中所述,您可以使用一些预处理器定义来更改此行为:
在扩展名的所有文件中,定义PY_ARRAY_UNIQUE_SYMBOL一个唯一的变量,该变量不太可能与其他扩展名冲突。在变量名中包括扩展的模块名将是一个好主意。
PY_ARRAY_UNIQUE_SYMBOL
在除您要调用的文件之外的每个文件中import_array,定义符号NO_IMPORT_ARRAY
NO_IMPORT_ARRAY
必须先定义这些符号,然后arrayobject.h才能使它们生效。
arrayobject.h