如何从 C 扩展 return Python 枚举?
How to return a Python Enum from C extension?
我正在为包装 C 库的 Python 编写 C 扩展。 C 库有一些枚举类型,我已经为这些写了相应的 IntEnum
s,例如:
from enum import IntEnum
# _enum_consts is a C extension creating Python
# constants for the library's enum constants
from . import _enum_consts
class Align(IntEnum):
DEFAULT = _enum_consts.MD_ALIGN_DEFAULT
LEFT = _enum_consts.MD_ALIGN_LEFT
CENTER = _enum_consts.MD_ALIGN_CENTER
RIGHT = _enum_consts.MD_ALIGN_RIGHT
现在,在我包装库主要功能的 C 扩展中,一些库函数 return 枚举类型,我想将它们转换为 Python IntEnum
值。 (我意识到我可以简单地 return 直接 int
并且它们可以成功地与 IntEnum
进行比较,但是这种做法违背了目的。)
问题是,如何将 IntEnum
值创建为 return?我能找到的所有示例都是将 C 枚举常量转换为带有 PyModule_AddIntConstant()
的 Python 常量(这正是 _enum_consts
扩展模块所做的)。
首先,带有 IntEnum
的模块需要导入到扩展模块中(如果您还没有这样做的话)。在模块初始化函数中:
PyMODINIT_FUNC PyInit_someextension(void)
{
// ...
PyObject *m;
m = PyModule_Create(&someextension_module);
if (m == NULL) {
return NULL;
}
// ...
// Import the enums module, where "somepackage.enums"
// is the full name of the enums module
PyObject *enums = PyImport_ImportModule("somepackage.enums");
if (enums == NULL) {
Py_DECREF(m);
return NULL;
}
Py_DECREF(enums);
return m;
}
然后,在你想实例化一个IntEnum
值的地方,首先再次获取它所在的模块:
PyObject *enums = PyImport_AddModule("somepackage.enums");
if (enums == NULL) {
// Return NULL, -1, etc. to signal an exception
}
紧接着,从模块中获取您的 IntEnum
类型(在本例中为 Align
):
PyObject *align_enum = PyObject_GetAttrString(enums, "Align");
if (align_enum == NULL) {
// Return NULL, -1, etc. to signal an exception
}
然后,您可以创建您需要的实例。 align
这里将是从 IntEnum
中获取的值。我们通过调用类型对象来做到这一点,就像我们在常规 Python 中所做的那样(注意 PyObject_CallFunction
实际上并不限于调用函数——它可以调用任何可调用对象):
PyObject *align_instance = PyObject_CallFunction(align_enum, "(i)", align);
if (align_instance == NULL) {
Py_DECREF(align_enum);
// Return NULL, -1, etc. to signal an exception
}
现在,align_instance
包含 IntEnum
到 return 的实例。请务必在 return 之前 Py_DECREF(align_enum)
。
我正在为包装 C 库的 Python 编写 C 扩展。 C 库有一些枚举类型,我已经为这些写了相应的 IntEnum
s,例如:
from enum import IntEnum
# _enum_consts is a C extension creating Python
# constants for the library's enum constants
from . import _enum_consts
class Align(IntEnum):
DEFAULT = _enum_consts.MD_ALIGN_DEFAULT
LEFT = _enum_consts.MD_ALIGN_LEFT
CENTER = _enum_consts.MD_ALIGN_CENTER
RIGHT = _enum_consts.MD_ALIGN_RIGHT
现在,在我包装库主要功能的 C 扩展中,一些库函数 return 枚举类型,我想将它们转换为 Python IntEnum
值。 (我意识到我可以简单地 return 直接 int
并且它们可以成功地与 IntEnum
进行比较,但是这种做法违背了目的。)
问题是,如何将 IntEnum
值创建为 return?我能找到的所有示例都是将 C 枚举常量转换为带有 PyModule_AddIntConstant()
的 Python 常量(这正是 _enum_consts
扩展模块所做的)。
首先,带有 IntEnum
的模块需要导入到扩展模块中(如果您还没有这样做的话)。在模块初始化函数中:
PyMODINIT_FUNC PyInit_someextension(void)
{
// ...
PyObject *m;
m = PyModule_Create(&someextension_module);
if (m == NULL) {
return NULL;
}
// ...
// Import the enums module, where "somepackage.enums"
// is the full name of the enums module
PyObject *enums = PyImport_ImportModule("somepackage.enums");
if (enums == NULL) {
Py_DECREF(m);
return NULL;
}
Py_DECREF(enums);
return m;
}
然后,在你想实例化一个IntEnum
值的地方,首先再次获取它所在的模块:
PyObject *enums = PyImport_AddModule("somepackage.enums");
if (enums == NULL) {
// Return NULL, -1, etc. to signal an exception
}
紧接着,从模块中获取您的 IntEnum
类型(在本例中为 Align
):
PyObject *align_enum = PyObject_GetAttrString(enums, "Align");
if (align_enum == NULL) {
// Return NULL, -1, etc. to signal an exception
}
然后,您可以创建您需要的实例。 align
这里将是从 IntEnum
中获取的值。我们通过调用类型对象来做到这一点,就像我们在常规 Python 中所做的那样(注意 PyObject_CallFunction
实际上并不限于调用函数——它可以调用任何可调用对象):
PyObject *align_instance = PyObject_CallFunction(align_enum, "(i)", align);
if (align_instance == NULL) {
Py_DECREF(align_enum);
// Return NULL, -1, etc. to signal an exception
}
现在,align_instance
包含 IntEnum
到 return 的实例。请务必在 return 之前 Py_DECREF(align_enum)
。