如何在Python C扩展程序中修复“ SystemError:<内置函数=“”'name'=“”>返回NULL而未设置错误”

时间:2018-12-29 19:03:57

标签: c python-3.x

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

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

我认为问题出在Sleep()函数;删除“ Sleep(1000)”部分或将其放置在“ printf(“从C线程打印... \ 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

结果应为: “从C线程打印...” 10次,每个语句之间间隔1秒。

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

1 个答案:

答案 0 :(得分:1)

发生错误的原因是:Python扩展功能必须具有特定的C原型:

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

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

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

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

您的代码没有显式强制转换,因此您必须获得

的警告
{"gen_nums", gen_nums, METH_VARARGS, "This is a threading test"},

无论如何,如果有显式强制转换,则该程序在Python尝试调用您的函数gen_nums()之前仍然是正确的程序,因为Python会这样做< strong>好像其原型是

PyObject *gen_nums(PyObject *, PyObject *);

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

  
      
  1. 指向一种类型的函数的指针可以转换为指向另一种类型的函数的指针,然后再返回;结果应等于原始指针。 如果使用转换后的指针来调用其类型与引用的类型不兼容的函数,则行为是不确定的。
  2.   

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


关于如何修复此问题,请务必阅读并理解Chapter 1 of 1. Extending Python with C or C++-里面有很多细节,但是其中修复了此简单功能所需的一切。如果您被卡住,请问其他问题,但请确实参考有问题的文档。

该功能的解决方案是将其写为

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;
}