将 .h 文件中的常量导入 python
Posted
技术标签:
【中文标题】将 .h 文件中的常量导入 python【英文标题】:Import constants from .h file into python 【发布时间】:2013-02-23 23:23:08 【问题描述】:我一直在寻找这个问题的简单答案,但似乎找不到。我宁愿远离 Python 2.6/2.7 中尚未包含的任何外部库。
我有 2 个类似于以下的 c 头文件:
//constants_a.h
const double constant1 = 2.25;
const double constant2 = -0.173;
const int constant3 = 13;
...
//constants_b.h
const double constant1 = 123.25;
const double constant2 = -0.12373;
const int constant3 = 14;
...
我有一个 python 类,我想将这些常量导入:
#pythonclass.py
class MyObject(object):
def __init(self, mode):
if mode is "a":
# import from constants_a.h, like:
# self.constant1 = constant1
# self.constant2 = constant2
elif mode is "b":
# import from constants_b.h, like:
# self.constant1 = constant1
# self.constant2 = constant2
...
我也有使用常量的 c 代码,类似于:
//computations.c
#include <stdio.h>
#include <math.h>
#include "constants_a.h"
// do some calculations, blah blah blah
如何将头文件中的常量导入 Python 类?
头文件 constants_a.h 和 constants_b.h 的原因是我使用 python 来做大部分使用常量的计算,但有一次我需要使用 C 来做更优化的计算。此时我正在使用 ctypes 将 c 代码包装到 Python 中。我想让常量远离代码,以防万一我需要更新或更改它们,并使我的代码更加简洁。我不知道是否有助于注意我也在使用 NumPy,但除此之外,没有其他非标准 Python 扩展。我也愿意接受有关此程序的设计或架构的任何建议。
【问题讨论】:
抱歉这么晚的反馈大家。我有一个新的、紧急的项目正在处理,不得不把它放在次要位置。目前,我被困在丛和埃米利奥的答案之间。我可能会更倾向于 Cong 的 ctypes 实现,但我仍然非常喜欢使用re
的解析方法。感谢大家的好主意!
【参考方案1】:
一般来说,在 C 头文件中定义变量的风格很差。头文件应该只声明对象,将它们的定义留给适当的“.c”源代码文件。
您可能想做的一件事是声明库全局常量,如 extern const whatever_type_t foo;
并在 C 代码中的某处定义(或“实现”)它们(即为它们分配值)(确保您只这样做一次)。
不管怎样,让我们忽略你是怎么做的的。假设您已经定义了常量并让它们的符号在您的共享对象文件“libfoo.so”中可见。假设您想从 Python 代码访问符号 pi
,在 libfoo 中定义为 extern const double pi = 3.1415926;
。
现在您通常使用ctypes
在 Python 中加载您的目标文件,如下所示:
>>> import ctypes
>>> libfoo = ctypes.CDLL("path/to/libfoo.so")
但是你会看到ctypes
认为libfoo.pi
是一个函数,而不是常量数据的符号!
>>> libfoo.pi
<_FuncPtr object at 0x1c9c6d0>
要访问它的值,你必须做一些相当尴尬的事情——将ctypes
认为是一个函数back 转换为一个数字。
>>> pi = ctypes.cast(foo.pi, ctypes.POINTER(ctypes.c_double))
>>> pi.contents.value
3.1415926
在 C 行话中,这隐约对应于以下发生的事情:您有一个 const double pi
,但有人强迫您仅通过函数指针使用它:
typedef int (*view_anything_as_a_function_t)(void);
view_anyting_as_a_function_t pi_view = π
您如何处理指针pi_view
以使用pi
的值?您将其转换为 const double *
并取消引用它:*(const double *)(pi_view)
。
所以这一切都很尴尬。也许我遗漏了一些东西,但我相信这是ctypes
模块的设计——它主要用于进行外部函数调用,而不是用于访问“外部”数据。在可加载库中导出纯数据符号可以说是很少见的。
如果常量只是 C 宏定义,这将不起作用。通常,您无法从外部访问宏定义的数据。它们在编译时进行宏扩展,在生成的库文件中不会留下可见符号,除非您还在 C 代码中导出它们的宏值。
【讨论】:
好吧,我刚刚发现了一种不尴尬的方法:python.net/crew/theller/ctypes/… 你可以在 Python 代码中尝试pi = ctypes.c_double(libfoo, "pi")
;这将返回一个ctypes.c_double
实例,您可以通过pi.value
访问它的值。
感谢丛非常详细的回答。我认为访问实例的值将适合我的工作。这是一项科学努力,而不是实际的软件版本,所以我要简单明了。不过,我很好奇你会认为什么是定义常量的更好风格。请记住,这些常量应该在一个单独的文件中,以便快速编辑或更改它们的值是一件简单的事情。【参考方案2】:
我建议使用正则表达式(re
模块)从文件中解析出你想要的信息。
构建一个完整的 C 解析器将是巨大的,但如果您只使用变量并且文件相当简单/可预测/受控制,那么您需要编写的内容很简单。
请注意“陷阱”工件,例如注释掉的代码!
【讨论】:
看看 Tools/Scripts/h2py.py 脚本,自 1992 年以来修订。 感谢 Emilio 和 eryksun。直到我看到丛的回答,我才相信这是要走的路。为此目的编写一个小型解析器也是一个很好的练习。 @ManilaThrilla:不客气。我已阅读并赞成 Cong 的答案,它非常有用。由于经过这么长时间不太可能出现更好的答案,我建议您将 Cong 标记为已接受。 我实际上使用了这个实现而不是Cong的。我发现这更适应更改/添加常量和参数。因此,我现在选择这个作为最佳答案。【参考方案3】:我建议使用 Python 和 C 程序都可读的某种配置文件,而不是在标头中存储常量值。例如。一个简单的 csv、ini 文件,甚至是您自己的“键:值”对的简单格式。并且每次您想更改其中一个值时都无需重新编译 C 程序 :)
【讨论】:
这听起来不错,但我想我将不得不为 C 程序创建一个解析器,我不想这样做,或者使用像 LibConfig 这样的库,我也不想这样做想要做。不过,不必每次都重新编译 C 程序会很好。你有这个配置文件的例子吗? 以 JSON 格式的配置为例。受 Python 标准库的支持,json.org 上提供了许多 C 的实现@【参考方案4】:我会投票给 emilio,但我缺乏代表!
尽管您已要求避免使用其他非标准库,但您不妨看看 Cython(Cython:C-Extensions for Python www.cython.org/),它提供了 Python 编码的灵活性和原始的C/C++ 编译代码的执行速度。
通过这种方式,您可以使用常规 Python 处理所有内容,但使用其内置的 C 类型处理昂贵的代码元素。然后,您也可以将您的 Python 代码转换为 .c 文件(或者只是自己包装外部 C 库。),然后可以将其编译为二进制文件。对于数值例程,我已经实现了高达 10 倍的加速。我也相信 NumPy 会使用它。
【讨论】:
好吧,我最初在 Cython 中实现了我的代码,但就像我在问题中所说的那样,我现在使用 ctypes 来保持可移植性。问题是标准 Python 库中没有 Cython,但 ctypes 是。但你是对的,Cython 很棒而且非常易于使用。我确实发现 ctypes 比 cython 快几微秒。以上是关于将 .h 文件中的常量导入 python的主要内容,如果未能解决你的问题,请参考以下文章
将python模块导入databricks中的python脚本