【问题标题】:Why might a C-based Python extension always return the same value?为什么基于 C 的 Python 扩展总是返回相同的值?
【发布时间】:2020-12-27 06:58:02
【问题描述】:

以下代码看起来非常简单。一个整数被传递给 Python 中的函数,该函数在 C 中创建一个 PyList,然后填充它:

hello.c

#include <Python.h>

PyObject* getlist(int *len)
{
    printf("Passed to C: %d\n", *len);
    PyObject *dlist = PyList_New(*len);
    double num = 0.1;
    for (int i = 0; i < *len; i++)
    {
        PyList_SetItem(dlist, i, PyFloat_FromDouble(num));
        num += 0.1;
    }

    return dlist;
}

static char helloworld_docs[] =
   "Fill docs where possible\n";

static PyMethodDef helloworld_funcs[] = {
   {"getlist", (PyCFunction)getlist, METH_VARARGS, helloworld_docs},
   {NULL}
};

static struct PyModuleDef Helloworld =
{
    PyModuleDef_HEAD_INIT,
    "Helloworld", // module name
    "NULL", // module documentation
    -1,   /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */
    helloworld_funcs
};

PyMODINIT_FUNC PyInit_helloworld(void)
{
    return PyModule_Create(&Helloworld);
}

setup.py

from distutils.core import setup
from distutils.extension import Extension

setup(name='helloworld', 
      version='1.0', 
      ext_modules=[Extension('helloworld', ['hello.c'])])

usepkg.py

#!/usr/bin/python
import sys
import helloworld
print("Input to Python:", sys.argv[1])
print (helloworld.getlist(sys.argv[1]))

我使用构建和安装

python3 setup.py build
python3 setup.py install

我没有发现任何错误。

当我测试它时会发生奇怪的行为。例如:

python3 usepkg.py 4

无论我作为参数给出什么值,输出总是相同的:

Input to Python: 4
Passed to C: 6
[0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6]

传递给 C 的值始终为 6。无论输入 agument 是 int 还是 Py_ssize_t,这都是相同的。我错过了什么?

【问题讨论】:

  • getlist 是 PyObject,难道不应该有至少两个参数吗?第一个是对自身的引用,第二个是参数列表。
  • 来自文档:“从 Python 中的参数列表(例如,单个表达式“ls -l”)到传递给 C 函数的参数有一个直接的翻译。C 函数总是有两个参数,通常命名为 self 和 args。"

标签: python c python-3.x cpython


【解决方案1】:

我很惊讶在构建时这里没有警告,函数的类型不应该是它们的原始类型,而是PyObject* - 然后你将解析类型并执行你的函数

以下是对您的功能的调整:

PyObject* getlist(PyObject* self, PyObject* args)
{
    int len;
    if (!PyArg_ParseTuple(args, "i", &len)) {
        return NULL;
    }
    printf("Passed to C: %d\n", len);
    PyObject *dlist = PyList_New(len);
    double num = 0.1;
    for (int i = 0; i < len; i++)
    {
        PyList_SetItem(dlist, i, PyFloat_FromDouble(num));
        num += 0.1;
    }

    return dlist;
}

更多信息可以在parsing arguments and building values文档中找到


你得到的数字很可能是selfPyObject*-&gt;ob_refcount 中的值(对C 模块的引用数)

在我的情况下,我看到的是 4 而不是 6,尽管我可能使用不同版本的 python 和/或调用方法

【讨论】:

  • 我猜是因为 PyCFunction{"getlist", (PyCFunction)getlist, METH_VARARGS, helloworld_docs}, 中的演员表不会有任何警告。您对getList 的定义不需要IIRC 演员表。因此,如果可能的话,最好不要使用 cast 以避免像 OP 这样的问题。
  • 所以理论上,如果函数是正确的,我在定义我的方法时应该不需要强制转换?
猜你喜欢
  • 2013-09-20
  • 1970-01-01
  • 1970-01-01
  • 2014-05-18
  • 2018-09-24
  • 1970-01-01
  • 2016-02-18
  • 2020-04-06
  • 2015-06-22
相关资源
最近更新 更多