为什么 PyLong_AsUnsignedLongLong 函数无法转换 numpy.uint64 元素,而 PyLong_AsLongLong 成功?
Why does PyLong_AsUnsignedLongLong function fail to convert a numpy.uint64 element, whereas PyLong_AsLongLong succeeds?
我正在为 Python 开发一个 C 扩展,它实现了一个方法来转换 numpy 元素列表(在本例中为 numpy.uint64
) 到 unsigned long long
C 类型 (使用 PyLong_AsUnsignedLongLong
函数)。然后对列表的元素求和,并将所得总和返回到 Python 层。
为了创建模块,我在 testmodule.c
:
中编写了这段代码
#include <Python.h>
static PyObject *method_sum_list_u64(PyObject *self, PyObject *args);
static PyMethodDef testmoduleMethods[] = {
{"sum_list_u64", method_sum_list_u64, METH_VARARGS, "docs"},
{NULL, NULL, 0, NULL}
};
static struct PyModuleDef testmodule = {
PyModuleDef_HEAD_INIT,
"testmodule",
"docs",
-1,
testmoduleMethods
};
PyMODINIT_FUNC PyInit_testmodule(void) {
return PyModule_Create(&testmodule);
}
这是我的方法:
static PyObject *method_print_list_u64(PyObject *self, PyObject *args) {
uint64_t sum = 0;
PyObject *input_list;
if (!PyArg_ParseTuple(args, "O!", &PyList_Type, &input_list))
{
return NULL;
}
Py_ssize_t data_points = PyList_Size(input_list);
for (Py_ssize_t i = 0; i < data_points; i++)
{
PyObject *item = PyList_GetItem(input_list, i);
sum += PyLong_AsUnsignedLongLong(item);
}
return PyLong_FromUnsignedLongLong(sum);
}
我的 setup.py
文件:
from setuptools import setup, Extension
def main():
setup(name="testmodule",
version="1.0.1",
description="Python interface for the testmodule C library function",
ext_modules=[Extension("testmodule", ["testmodule.c"])])
if __name__ == "__main__":
main()
还有一个名为 mytest.py
的简单测试脚本:
import numpy as np
import testmodule
input_list = [np.uint64(1000)]
print(testmodule.sum_list_u64(input_list))
重现我的错误 运行:
$ python setup.py install
$ python mytest.py
TypeError: an integer is required
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "mytest.py", line 5, in <module>
print(testmodule.sum_list_u64(input_list))
SystemError: <built-in function sum_list_u64> returned a result with an error set
现在,如果我用 PyLong_AsLongLong
替换 PyLong_AsUnsignedLongLong
一切正常。为什么 PyLong_AsUnsignedLongLong
失败而 PyLong_AsLongLong
没有?
isinstance(numpy.uint64(5),int)
returns False
- Numpy 标量整数类型不是 Python 整数类型的子类。它们具有不同的内部结构,因此不能与期望 Python 整数的 C API 函数一起使用(值得注意的是,Python 整数类型可以处理任意大的整数,而 Numpy 类型是有限的到内部使用的 C 类型的大小)。
如果您阅读 the documentation,您会看到 PyLong_AsLongLong
将尝试转换为 Python int,而 PyLong_AsUnsignedLongLong
将仅接受 [= 的实例实例14=]。我不知道为什么会这样,我认为这不会很容易找出来。但是,这是记录在案的行为,它解释了您所看到的内容。
几个选项:
首先将所有内容转换为Python PyLongObject
。这个方法可能是最健壮的(它会接受你传递给它的任何合理的数据类型),但确实涉及创建中间 Python 对象(当你完成它们时记得 DECREF
它们) .使用 PyObject_CallFunctionObjArgs(PyLongObject, item, NULL)
(尽管其他选项也可用)。然后你可以使用 PyLong_AsUnsignedLongLong
因为你现在肯定有正确的类型。
如果您总是传递 Numpy 标量类型,则使用 Numpy 函数 PyArray_ScalarAsCtype 快速直接地从中获取数据。您 将 需要一些错误和类型检查以确保您确实收到了 numpy.uint64
.
考虑传递一个 Numpy 数组而不是 numpy.uint64
的列表。使用孤立的 Numpy 标量类型并不清楚你得到了什么,而数组在内部存储为 uint64_t
的 C 数组,你可以快速迭代。
考虑删除 numpy.uint64
s 并在列表中只使用普通的 Python int
s 因为我看不到你使用 Numpy 有什么好处类型。然后你可以调用 PyLong_AsUnsignedLongLong
.
您的函数中还缺少 所有 错误检查。大多数 C API 调用都有一个 return 值表示错误(通常是 NULL
但有时不同)。检查这一点很重要。不要跳过它!
我正在为 Python 开发一个 C 扩展,它实现了一个方法来转换 numpy 元素列表(在本例中为 numpy.uint64
) 到 unsigned long long
C 类型 (使用 PyLong_AsUnsignedLongLong
函数)。然后对列表的元素求和,并将所得总和返回到 Python 层。
为了创建模块,我在 testmodule.c
:
#include <Python.h>
static PyObject *method_sum_list_u64(PyObject *self, PyObject *args);
static PyMethodDef testmoduleMethods[] = {
{"sum_list_u64", method_sum_list_u64, METH_VARARGS, "docs"},
{NULL, NULL, 0, NULL}
};
static struct PyModuleDef testmodule = {
PyModuleDef_HEAD_INIT,
"testmodule",
"docs",
-1,
testmoduleMethods
};
PyMODINIT_FUNC PyInit_testmodule(void) {
return PyModule_Create(&testmodule);
}
这是我的方法:
static PyObject *method_print_list_u64(PyObject *self, PyObject *args) {
uint64_t sum = 0;
PyObject *input_list;
if (!PyArg_ParseTuple(args, "O!", &PyList_Type, &input_list))
{
return NULL;
}
Py_ssize_t data_points = PyList_Size(input_list);
for (Py_ssize_t i = 0; i < data_points; i++)
{
PyObject *item = PyList_GetItem(input_list, i);
sum += PyLong_AsUnsignedLongLong(item);
}
return PyLong_FromUnsignedLongLong(sum);
}
我的 setup.py
文件:
from setuptools import setup, Extension
def main():
setup(name="testmodule",
version="1.0.1",
description="Python interface for the testmodule C library function",
ext_modules=[Extension("testmodule", ["testmodule.c"])])
if __name__ == "__main__":
main()
还有一个名为 mytest.py
的简单测试脚本:
import numpy as np
import testmodule
input_list = [np.uint64(1000)]
print(testmodule.sum_list_u64(input_list))
重现我的错误 运行:
$ python setup.py install
$ python mytest.py
TypeError: an integer is required
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "mytest.py", line 5, in <module>
print(testmodule.sum_list_u64(input_list))
SystemError: <built-in function sum_list_u64> returned a result with an error set
现在,如果我用 PyLong_AsLongLong
替换 PyLong_AsUnsignedLongLong
一切正常。为什么 PyLong_AsUnsignedLongLong
失败而 PyLong_AsLongLong
没有?
isinstance(numpy.uint64(5),int)
returns False
- Numpy 标量整数类型不是 Python 整数类型的子类。它们具有不同的内部结构,因此不能与期望 Python 整数的 C API 函数一起使用(值得注意的是,Python 整数类型可以处理任意大的整数,而 Numpy 类型是有限的到内部使用的 C 类型的大小)。
如果您阅读 the documentation,您会看到 PyLong_AsLongLong
将尝试转换为 Python int,而 PyLong_AsUnsignedLongLong
将仅接受 [= 的实例实例14=]。我不知道为什么会这样,我认为这不会很容易找出来。但是,这是记录在案的行为,它解释了您所看到的内容。
几个选项:
首先将所有内容转换为Python
PyLongObject
。这个方法可能是最健壮的(它会接受你传递给它的任何合理的数据类型),但确实涉及创建中间 Python 对象(当你完成它们时记得DECREF
它们) .使用PyObject_CallFunctionObjArgs(PyLongObject, item, NULL)
(尽管其他选项也可用)。然后你可以使用PyLong_AsUnsignedLongLong
因为你现在肯定有正确的类型。如果您总是传递 Numpy 标量类型,则使用 Numpy 函数 PyArray_ScalarAsCtype 快速直接地从中获取数据。您 将 需要一些错误和类型检查以确保您确实收到了
numpy.uint64
.考虑传递一个 Numpy 数组而不是
numpy.uint64
的列表。使用孤立的 Numpy 标量类型并不清楚你得到了什么,而数组在内部存储为uint64_t
的 C 数组,你可以快速迭代。考虑删除
numpy.uint64
s 并在列表中只使用普通的 Pythonint
s 因为我看不到你使用 Numpy 有什么好处类型。然后你可以调用PyLong_AsUnsignedLongLong
.
您的函数中还缺少 所有 错误检查。大多数 C API 调用都有一个 return 值表示错误(通常是 NULL
但有时不同)。检查这一点很重要。不要跳过它!