我正在使用numpy的C API来编写一些用于矩阵计算的函数。今天我想将我的函数的某些部分移动到一个单独的.c文件中,并使用标题来声明它们。现在我有一个与numpy的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
,然后加载模块并调用函数:
test.py
import mytest
print(mytest.my_test_function())
如果我从init函数中删除import_array
,则会出现段错误,这是许多邮件列表和论坛中报告的行为。
现在我只想从 mytest.c 中删除整个函数my_sub_function
,并将其移到名为 sub.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;
}
新的 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模块拆分成几个源文件的“干净”方式。
答案 0 :(得分:9)
默认情况下,import_array
例程只会在单个文件中提供NumPy C API。这是因为它通过存储在静态全局变量中的函数指针表来工作(即不导出,只在同一文件中可见)。
作为mentioned in the documentation,您可以使用一些预处理器定义来更改此行为:
在您的扩展程序的所有文件中,将PY_ARRAY_UNIQUE_SYMBOL
定义为一个不太可能与其他扩展程序冲突的唯一变量。在变量名中包含扩展程序的模块名称是个不错的主意。
在每个文件中,除了您拨打import_array
的文件外,请定义符号NO_IMPORT_ARRAY
在包含arrayobject.h
之前,需要先定义这些符号才能使它们生效。