2

工具:Python3.7(64 位)、Visual C++ 10.0 我正在尝试为 Python 创建一个 C 扩展。首先,我正在测试一个简单的 C 代码,它打印一个字符串并在for循环中调用 Sleep() 函数。但是,当我从 Python 对这个名为 gen_nums 的 C 函数进行简单调用时,我收到以下错误:

“SystemError:内置函数 gen_nums 返回 NULL 而未设置错误”

我认为问题出在 Sleep() 函数上;删除“Sleep(1000)”部分或将其放在“printf("Printed from C thread...\n")”之前可以消除此错误。我查看了 Sleep() 的文档,但找不到任何有用的东西。

C代码:

#include <Python.h>

static void gen_nums() {
    int i;
    for(i = 0; i < 10; i++) {
        printf("Printed from C thread...\n");
        Sleep(1000);
    }
}
static PyMethodDef gen_numsmethods[] = {
    {"gen_nums", gen_nums, METH_VARARGS, "This is a threading test"},
    {NULL, NULL, 0, NULL}
};
static struct PyModuleDef threadmod = {
    PyModuleDef_HEAD_INIT,
    "threadrun",
    "This is a thread test module",
    -1,
    gen_numsmethods
};

PyMODINIT_FUNC PyInit_threadrun(void) {
    return PyModule_Create(&threadmod);
}

Python 调用:

threadrun.gen_nums() \\ the C module is called threadrun

结果应该是:"Printed from C thread..." 10 次,每条语句之间有 1 秒的间隔。

但是,程序打印了 10 次语句,然后显示上述错误。

4

1 回答 1

3

错误的原因是这样的:Python扩展函数必须有一定的C原型:

PyObject *func(PyObject *self, PyObject *args)

方法槽包含类型的函数指针

PyObject *(*)(Pyobject *, PyObject *)

旧的方法是强制将函数强制转换为这种指针类型以存储到方法槽中。void (*)()显式强制转换将使to的转换错误静音PyObject *(*)(Pyobject *, PyObject *)。转换是有效的,但需要显式转换。如果不存在显式转换,则 C 编译器必须发出诊断消息

您的代码没有显式转换,因此您必须收到警告

{"gen_nums", gen_nums, METH_VARARGS, "This is a threading test"},

在任何情况下,如果有一个显式的强制转换,在 Python 尝试调用你的函数之前,该程序仍然是一个正确的程序gen_nums(),因为 Python 会这样做,就好像它的原型是

PyObject *gen_nums(PyObject *, PyObject *);

现在C 标准说虽然到目前为止一切都很好,但从现在开始程序的行为未定义的,因为C11 6.3.2.3

  1. 指向一种类型的函数的指针可以转换为指向另一种类型的函数的指针,然后再返回;结果应与原始指针比较。如果转换后的指针用于调用类型与引用类型不兼容的函数,则行为未定义。

您的函数返回void,即什么都没有,但是您问“为什么它只在其中返回NULL时才返回Sleep()。原因是“未定义的行为”。


至于如何解决这个问题,请阅读并理解1. 使用 C 或 C++ 扩展 Python 的第 1 章- 里面有很多细节,但修复这个简单功能所需的一切都在里面详述。如果您遇到困难,请提出进一步的问题,但请参阅问题中的文档。

函数的修复方法是将其编写为

static PyObject *gen_nums(PyObject *self, PyObject *args) {
    int i;
    for(i = 0; i < 10; i++) {
        printf("Printed from C thread...\n");
        Sleep(1000);
    }
    Py_RETURN_NONE;
}
于 2018-12-29T19:34:33.443 回答