обычно требуется явно указать каталог, в котором лежит заголовочный файл Python.h (в gcc это делается опцией–I)
чтобы получившийся исполняемый файл мог корректно предоставлять имена для динамически загружаемых модулей, требуется передать компоновщику опцию–E: это можно сделать из gcc с помощью опции–Wl, — E. (В противном случае, модуль time, а это модуль расширения в виде динамически загружаемого модуля, не будет работать из–за того, что не увидит имен, определенных в libpython)
Здесь же следует сделать еще одно замечание: программа, встраивающая Python, не должна много раз выполнять Py_Initialize() и Py_Finalize(), так как это может приводить к утечке памяти. Сам же интерпретатор Python очень стабилен и в большинстве случаев не дает утечек памяти.
Использование SWIG
SWIG (Simplified Wrapper and Interface Generator, упрощенный упаковщик и генератор интерфейсов) - это программное средства, сильно упрощающее (во многих случаях — автоматизирующее) использование библиотек, написанных на C и C++, а также на других языках программирования, в том числе (не в последнюю очередь!) на Python. Нужно отметить, что SWIG обеспечивает достаточно полную поддержку практически всех возможностей C++, включая предобработку, классы, указатели, наследование и даже шаблоны C++. Последнее очень важно, если необходимо создать интерфейс к библиотеке шаблонов.
Пользоваться SWIG достаточно просто, если уметь применять компилятор и компоновщик (что в любом случае требуется при программировании на C/C++).
Простой пример использования SWIG
Предположим, что есть программа на C, реализующая некоторую функцию (пусть это будет вычисление частоты появления различных символов в строке):
Листинг
/* File : freq.c */
#include <stdlib.h>
int * frequency(char s[]) {
int *freq;
char *ptr;
freq = (int*)(calloc(256, sizeof(int)));
if (freq != NULL)
for (ptr = s; *ptr; ptr++)
freq[*ptr] += 1;
return freq;
}
Для того чтобы можно было воспользоваться этой функцией из Python, нужно написать интерфейсный файл (расширение .i) примерно следующего содержания:
Листинг
/* File : freq.i */
%module freq
%typemap(out) int * {
int i;
$result = PyTuple_New(256);
for(i=0; i<256; i++)
PyTuple_SetItem($result, i, PyLong_FromLong($1[i]));
free($1);
}
extern int * frequency(char s[]);
Интерфейсные файлы содержат инструкции самого SWIG и фрагменты C/C++-кода, возможно, с макровключениями (в примере выше: $result, $1). Следует заметить, что для преобразования массива целых чисел в кортеж элементов типа long, необходимо освободить память из–под исходного массива, в котором подсчитывались частоты.
Теперь (подразумевая, что используется компилятор gcc), создание модуля расширения может быть выполнено примерно так:
Листинг
swig–python freq.i
gcc–c–fpic freq_wrap.c freq.c -DHAVE_CONFIG_H
— I/usr/local/include/python2.3–I/usr/local/lib/python2.3/config
gcc–shared freq.o freq_wrap.o–o _freq.so
После этого в рабочем каталоге появляется файлы _freq.so и freq.py, которые вместе и дают доступ к требуемой функции:
Листинг
>>> import freq
>>> freq.frequency(«ABCDEF»)[60:75]
(0L, 0L, 0L, 0L, 0L, 1L, 1L, 1L, 1L, 1L, 1L, 0L, 0L, 0L, 0L)
Помимо этого, можно посмотреть на содержимое файла freq_wrap.c, который был порожден SWIG: в нем, среди прочих вспомогательных определений, нужных самому SWIG, можно увидеть что–то подобное проиллюстрированному выше примеру модуля md5. Вот фрагмент этого файла с определением обертки для функции frequency():
Листинг
extern int *frequency(char []);
static PyObject *_wrap_frequency(PyObject *self, PyObject *args) {
PyObject *resultobj;
char *arg1 ;
int *result;
if(!PyArg_ParseTuple(args,(char *)«s:frequency»,&arg1)) goto fail;
result = (int *)frequency(arg1);
{
int i;
resultobj = PyTuple_New(256);
for(i=0; i<256; i++)
PyTuple_SetItem(resultobj, i, PyLong_FromLong(result[i]));
free(result);
}
return resultobj;
fail:
return NULL;
}
В качестве упражнения, предлагается сопоставить это определение с файлом freq.i и понять, что происходит внутри функции _wrap_frequency(). Подсказка: можно посмотреть еще раз комментарии к C–коду модуля md5.
Стоит еще раз напомнить, что в отличие от Python, в языке C/C++ управление памятью должно происходить в явном виде. Именно поэтому добавлена функция free() при преобразовании типа. Если этого не сделать, возникнут утечки памяти. Эти утечки можно обнаружить, при многократном выполнении функции:
Листинг
>>> import freq
>>> for i in xrange(1000000):
… dummy = freq.frequency(«ABCDEF»)
>>>
Если функция freq.frequency() имеет утечки памяти, выполняемый процесс очень быстро займет всю имеющуюся память.
Интеграция Python и других систем программирования
Язык программирования Python является сценарным языком, а значит его основное назначение — интеграция в единую систему разнородных программных компонентов. Выше рассматривалась (низкоуровневая) интеграция с C/C++-приложениями. Нужно заметить, что в большинстве случаев достаточно интеграции с использованием протокола. Например, интегрируемые приложения могут общаться через XML–RPC, SOAP, CORBA, COM, .NET и т.п. В случаях, когда приложения имеют интерфейс командной строки, их можно вызывать из Python и управлять стандартным вводом–выводом, переменными окружения. Однако есть и более интересные варианты интеграции.
Современное состояние дел по излагаемому вопросу можно узнать по адресу: http://www.python.org/moin/IntegratingPythonWithOtherLanguages
Java
Документация по Jython (это реализация Python на Java–платформе) отмечает, что Jython обладает следующими неоспоримыми преимуществами над другими языками, использующими Java–байт–код:
Jython–код динамически компилирует байт–коды Java, хотя возможна и статическая компиляция, что позволяет писать апплеты, сервлеты и т.п.;
Поддерживает объектно–ориентированную модель Java, в том числе, возможность наследовать от абстрактных Java–классов;
Jython является реализацией Python — языка с практичным синтаксисом, обладающего большой выразительностью, что позволяет сократить сроки разработки приложений в разы.
Правда, имеются и некоторые ограничения по сравнению с «обычным» Python. Например, Java не поддерживает множественного наследования, поэтому в некоторых версиях Jython нельзя наследовать классы от нескольких Java–классов (в тоже время, множественное наследование поддерживается для Python–классов).
Следующий пример (файл lines.py) показывает полную интеграцию Java–классов с интерпретатором Python:
Листинг
# Импортируются модули из Java
from java.lang import System
from java.awt import *
# А это модуль из Jython
import random
# Класс для рисования линий на рисунке
class Lines(Canvas):
# Реализация метода paint()
def paint(self, g):
X, Y = self.getSize().width, self.getSize().height
label.setText("%s x %s» % (X, Y))
for i in range(100):
x1, y1 = random.randint(1, X), random.randint(1, Y)
x2, y2 = random.randint(1, X), random.randint(1, Y)
g.drawLine(x1, y1, x2, y2)
# Метки, кнопки и т.п.
panel = Panel(layout=BorderLayout())
label = Label(«Size», Label.RIGHT)
panel.add(label, «North»)
button = Button(«QUIT», actionPerformed=lambda e: System.exit(0))
panel.add(button, «South»)
lines = Lines()