2024-09-29 22:25:47 发布
网友
我正在为如何在python C API中创建python枚举对象而苦苦挣扎。enum类已将tp_base分配给PyEnum_Type,因此它继承了enum。但是,我想不出一种方法来告诉枚举基类枚举中有哪些项。我想允许使用每个Python枚举提供的__members__属性从Python进行迭代和查找
tp_base
PyEnum_Type
__members__
谢谢,
杰尔
编辑说明:An answer on a very similar question详细说明了enum.Enum如何具有可替代使用的功能接口。这几乎肯定是正确的方法。我认为我在这里的回答是一个值得注意的有用的替代方法,尽管它可能不是这个问题的最佳解决方案
enum.Enum
我知道这个答案有点假,但这正是最好用Python编写的代码,在C API中,我们仍然可以访问完整的Python解释器。我的理由是,将所有内容完全保留在C中的主要原因是性能,而且创建枚举对象似乎不太可能是性能关键
我将给出三个版本,基本上取决于复杂程度
首先,最简单的情况是:枚举是完全已知的,并且是在编译时定义的。在这里,我们只需设置一个空的全局dict,运行Python代码,然后从全局dict中提取枚举:
PyObject* get_enum(void) { const char str[] = "from enum import Enum\n" "class Colour(Enum):\n" " RED = 1\n" " GREEN = 2\n" " BLUE = 3\n" ""; PyObject *global_dict=NULL, *should_be_none=NULL, *output=NULL; global_dict = PyDict_New(); if (!global_dict) goto cleanup; should_be_none = PyRun_String(str, Py_file_input, global_dict, global_dict); if (!should_be_none) goto cleanup; // extract Color from global_dict output = PyDict_GetItemString(global_dict, "Colour"); if (!output) { // PyDict_GetItemString does not set exceptions PyErr_SetString(PyExc_KeyError, "could not get 'Colour'"); } else { Py_INCREF(output); // PyDict_GetItemString returns a borrow reference } cleanup: Py_XDECREF(global_dict); Py_XDECREF(should_be_none); return output; }
其次,我们可能希望在运行时更改在C中定义的内容。例如,输入参数可能会选择枚举值。在这里,我将使用字符串格式将适当的值插入到字符串中。这里有很多选项:^ {< CD2> },^ {CD3}},C++标准库,使用Python字符串(也许是用另一个调用到Python代码中)。选择你最喜欢的
PyObject* get_enum_fmt(int red, int green, int blue) { const char str[] = "from enum import Enum\n" "class Colour(Enum):\n" " RED = %d\n" " GREEN = %d\n" " BLUE = %d\n" ""; PyObject *formatted_str=NULL, *global_dict=NULL, *should_be_none=NULL, *output=NULL; formatted_str = PyBytes_FromFormat(str, red, green, blue); if (!formatted_str) goto cleanup; global_dict = PyDict_New(); if (!global_dict) goto cleanup; should_be_none = PyRun_String(PyBytes_AsString(formatted_str), Py_file_input, global_dict, global_dict); if (!should_be_none) goto cleanup; // extract Color from global_dict output = PyDict_GetItemString(global_dict, "Colour"); if (!output) { // PyDict_GetItemString does not set exceptions PyErr_SetString(PyExc_KeyError, "could not get 'Colour'"); } else { Py_INCREF(output); // PyDict_GetItemString returns a borrow reference } cleanup: Py_XDECREF(formatted_str); Py_XDECREF(global_dict); Py_XDECREF(should_be_none); return output; }
显然,您可以对字符串格式做任意多或任意少的操作—我刚刚选择了一个简单的示例来说明这一点。与上一版本的主要区别是对PyBytes_FromFormat的调用以设置字符串,以及对PyBytes_AsString的调用,该调用从准备好的bytes对象中获取底层char*
PyBytes_FromFormat
PyBytes_AsString
bytes
char*
最后,我们可以在C Pythondict中准备enum属性并将其传入。这需要一些改变。实际上,我使用@AnttiHaapala的低级Python代码,但在调用__prepare__之后插入namespace.update(contents)
dict
__prepare__
namespace.update(contents)
PyObject* get_enum_dict(const char* key1, int value1, const char* key2, int value2) { const char str[] = "from enum import Enum\n" "name = 'Colour'\n" "bases = (Enum,)\n" "enum_meta = type(Enum)\n" "namespace = enum_meta.__prepare__(name, bases)\n" "namespace.update(contents)\n" "Colour = enum_meta(name, bases, namespace)\n"; PyObject *global_dict=NULL, *contents_dict=NULL, *value_as_object=NULL, *should_be_none=NULL, *output=NULL; global_dict = PyDict_New(); if (!global_dict) goto cleanup; // create and fill the contents dictionary contents_dict = PyDict_New(); if (!contents_dict) goto cleanup; value_as_object = PyLong_FromLong(value1); if (!value_as_object) goto cleanup; int set_item_result = PyDict_SetItemString(contents_dict, key1, value_as_object); Py_CLEAR(value_as_object); if (set_item_result!=0) goto cleanup; value_as_object = PyLong_FromLong(value2); if (!value_as_object) goto cleanup; set_item_result = PyDict_SetItemString(contents_dict, key2, value_as_object); Py_CLEAR(value_as_object); if (set_item_result!=0) goto cleanup; set_item_result = PyDict_SetItemString(global_dict, "contents", contents_dict); if (set_item_result!=0) goto cleanup; should_be_none = PyRun_String(str, Py_file_input, global_dict, global_dict); if (!should_be_none) goto cleanup; // extract Color from global_dict output = PyDict_GetItemString(global_dict, "Colour"); if (!output) { // PyDict_GetItemString does not set exceptions PyErr_SetString(PyExc_KeyError, "could not get 'Colour'"); } else { Py_INCREF(output); // PyDict_GetItemString returns a borrow reference } cleanup: Py_XDECREF(contents_dict); Py_XDECREF(global_dict); Py_XDECREF(should_be_none); return output; }
同样,这提供了一种相当灵活的方法,可以将值从C获取到生成的枚举中
为了进行测试,我使用了下面这个简单的Cython包装器——这只是为了完整性,以帮助人们尝试这些功能
cdef extern from "cenum.c": object get_enum() object get_enum_fmt(int, int, int) object get_enum_dict(char*, int, char*, int) def py_get_enum(): return get_enum() def py_get_enum_fmt(red, green, blue): return get_enum_fmt(red, green, blue) def py_get_enum_dict(key1, value1, key2, value2): return get_enum_dict(key1, value1, key2, value2)
重申一下:这个答案只在C API中有一部分,但我发现从C调用Python的方法有时对完全用C编写的“运行一次”代码很有效
这一点也不简单。{}是一个使用Python元类的Python类。可以在C中创建它,但它只是模拟C中构造Python代码的过程,最终结果是一样的,虽然它稍微加快了速度,但您很可能在每次程序运行中只运行一次代码
无论如何,这是可能的,但一点也不容易。我将演示如何在Python中执行此操作:
from enum import Enum class Color(Enum): RED = 1 GREEN = 2 BLUE = 3 print(Color) print(Color.RED)
同:
from enum import Enum name = 'Color' bases = (Enum,) enum_meta = type(Enum) namespace = enum_meta.__prepare__(name, bases) namespace['RED'] = 1 namespace['GREEN'] = 2 namespace['BLUE'] = 3 Color = enum_meta(name, bases, namespace) print(Color) print(Color.RED)
后者是需要翻译成C的代码
编辑说明:An answer on a very similar question详细说明了
enum.Enum
如何具有可替代使用的功能接口。这几乎肯定是正确的方法。我认为我在这里的回答是一个值得注意的有用的替代方法,尽管它可能不是这个问题的最佳解决方案我知道这个答案有点假,但这正是最好用Python编写的代码,在C API中,我们仍然可以访问完整的Python解释器。我的理由是,将所有内容完全保留在C中的主要原因是性能,而且创建枚举对象似乎不太可能是性能关键
我将给出三个版本,基本上取决于复杂程度
首先,最简单的情况是:枚举是完全已知的,并且是在编译时定义的。在这里,我们只需设置一个空的全局dict,运行Python代码,然后从全局dict中提取枚举:
其次,我们可能希望在运行时更改在C中定义的内容。例如,输入参数可能会选择枚举值。在这里,我将使用字符串格式将适当的值插入到字符串中。这里有很多选项:^ {< CD2> },^ {CD3}},C++标准库,使用Python字符串(也许是用另一个调用到Python代码中)。选择你最喜欢的
显然,您可以对字符串格式做任意多或任意少的操作—我刚刚选择了一个简单的示例来说明这一点。与上一版本的主要区别是对
PyBytes_FromFormat
的调用以设置字符串,以及对PyBytes_AsString
的调用,该调用从准备好的bytes
对象中获取底层char*
最后,我们可以在C Python
dict
中准备enum属性并将其传入。这需要一些改变。实际上,我使用@AnttiHaapala的低级Python代码,但在调用__prepare__
之后插入namespace.update(contents)
同样,这提供了一种相当灵活的方法,可以将值从C获取到生成的枚举中
为了进行测试,我使用了下面这个简单的Cython包装器——这只是为了完整性,以帮助人们尝试这些功能
重申一下:这个答案只在C API中有一部分,但我发现从C调用Python的方法有时对完全用C编写的“运行一次”代码很有效
这一点也不简单。{}是一个使用Python元类的Python类。可以在C中创建它,但它只是模拟C中构造Python代码的过程,最终结果是一样的,虽然它稍微加快了速度,但您很可能在每次程序运行中只运行一次代码
无论如何,这是可能的,但一点也不容易。我将演示如何在Python中执行此操作:
同:
后者是需要翻译成C的代码
相关问题 更多 >
编程相关推荐