从.h文件中导入常量到Python

16 投票
4 回答
31595 浏览
提问于 2025-04-17 18:24

我一直在寻找这个问题的简单答案,但似乎找不到。我希望尽量不使用任何不包含在Python 2.6/2.7中的外部库。

我有两个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扩展。我也欢迎任何关于这个程序设计或架构的建议。

4 个回答

2

我建议使用一种配置文件,这种文件可以被Python和C程序都能读取,而不是把常量值放在头文件里。比如,可以使用简单的csv文件、ini文件,或者你自己定义的简单的'键:值'格式。这样的话,每次想要更改某个值时,就不需要重新编译C程序了 :)

16

一般来说,在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")

但是你会发现 ctypeslibfoo.pi 当成了一个函数,而不是常量数据的符号!

>>> libfoo.pi
<_FuncPtr object at 0x1c9c6d0>

要获取它的值,你需要做一些比较麻烦的事情——把 ctypes 认为是函数的东西转换回一个数字。

>>> 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代码中也导出它们的宏值。

撰写回答