Импорт констант из файла .h в python
Я искал простой ответ на этот вопрос, но кажется, что я не могу его найти. Я бы предпочел держаться подальше от любых внешних библиотек, которые еще не включены в 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. Я также открыт для любых предложений относительно дизайна или архитектуры этой программы.
Ответы
Ответ 1
Я рекомендую использовать регулярные выражения (re
module) для анализа информации, которую вы хотите получить из файлов.
Построение полного анализатора C было бы огромным, но если вы используете только переменные, и файл достаточно прост/предсказуем/под контролем, то то, что вам нужно написать, прямолинейно.
Просто следите за артефактами "getcha", такими как код с комментариями!
Ответ 2
В общем случае определение переменных в файле заголовка C плохое. Файл заголовка должен только объявлять объекты, оставляя их определение для соответствующего файла исходного кода ".c".
Одна вещь, которую вы, возможно, захотите сделать, - объявить глобально-глобальные константы, такие как extern const whatever_type_t foo;
, и определить (или "реализовать" ) их (то есть присвоить им значения) где-нибудь в вашем коде C (убедитесь, что вы делаете это только один раз).
В любом случае, пусть игнорирует, как вы это делаете. Предположим, вы уже определили константы и сделали свои символы видимыми в вашем общем объектном файле libfoo.so. Предположим, вы хотите получить доступ к символу pi
, определенному как extern const double pi = 3.1415926;
в libfoo, из вашего кода Python.
Теперь вы обычно загружаете свой объектный файл в Python с помощью ctypes
следующим образом:
>>> import ctypes
>>> libfoo = ctypes.CDLL("path/to/libfoo.so")
Но тогда вы увидите ctypes
thinks libfoo.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_view
, чтобы использовать значение pi
? Вы отбрасываете его как const double *
и разыщите его: *(const double *)(pi_view)
.
Так что это все очень неудобно. Возможно, мне что-то не хватает, но я считаю, что это дизайн модуля ctypes
- он там главным образом для совершения внешних вызовов функций, а не для доступа к "чужим" данным. И экспортировать чистый символ данных в загружаемой библиотеке, возможно, редко.
И это не сработает, если константы являются только макроопределениями C. Как правило, вы не можете получить доступ к макроопределенным данным извне. Они макросрасширяются во время компиляции, не оставляя видимого символа в сгенерированном файле библиотеки, если вы также не экспортируете свои значения макросов в свой код C.
Ответ 3
Я бы рекомендовал использовать какой-то файл конфигурации, который можно читать как с помощью программы Python, так и с C, а не хранить постоянные значения в заголовках. Например. простой csv, ini файл или даже ваш собственный простой формат пар "ключ: значение". И не будет необходимости перекомпилировать программу C каждый раз, когда вы хотите изменить одно из значений:)
Ответ 4
Я бы проголосовал за emilio, но мне не хватает репутации!
Хотя вы просили избежать других нестандартных библиотек, вы можете взглянуть на Cython (Cython: C-Extensions для Python www.cython.org/), который предлагает гибкость кодирования Python и необработанную скорость выполнения C/С++ - скомпилированный код.
Таким образом, вы можете использовать обычный Python для всего, но обрабатывайте дорогие элементы кода, используя встроенные C-типы. Затем вы можете преобразовать свой код Python в файлы .c(или просто обернуть внешние C-библиотеки сами). Затем их можно скомпилировать в двоичный файл. Я достиг десятикратного ускорения, делающего это для числовых процедур. Я также считаю, что NumPy использует его.