将 .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;

您如何处理指针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 文件中的函数导入 Kivy 文件?

将python模块导入databricks中的python脚本

Python 导入其他文件中的类

玛丽冒险

使用导入的 makefile 将 Qt Creator .config 添加到 .h

如何将需要导入模块的python脚本转换为Windows中的可执行文件