Басқа бағдарламалау тілдерімен Python интеграциясы

 

Басқа бағдарламалау тілдерімен Python интеграциясы.
Дәрістің мақсаты: Басқа бағдарламалау тілдерімен Python интеграциясын қарастыру, талдау.
Тақырып бойынша қарастырылатын сұрақтар:
1. API.
2. Кеңейту модулін жазу.
3. SWIG пайдалану.

1. API.

Python тілінен қол жетімді Модульдер кеңейту модульдері (extension modules) есебінен кеңейтіледі. Кеңею модулдерін C немесе C++ тілінде жазуға және Python бағдарламаларынан шақыруға болады. Бұл дәрісте CPython деп аталатын Python жүзеге асыру туралы әңгіме болады (Jython, Java платформасында Python жүзеге асыру қарастырылмайды).
C тілін пайдалану қажеттілігі Python бағдарламаланған алгоритмі баяу жұмыс істесе, пайда болуы мүмкін. Мысалы, Numeric Модулінің массивтерімен жоғары өнімді операциялар (алдыңғы дәрістердің бірінде айтылған) C тілінде жазылған. Python үшін кеңейту модульдерін құру үшін қажетті мәліметтер стандартты құжаттамада, атап айтқанда «Python/C API Reference Manual» құжатында («Python/C API»анықтамалық нұсқаулығы) толық көлемде берілген. Мұнда API туралы егжей-тегжейлі мәліметтерсіз кеңейту модулін құрудың негізгі принциптері ғана қарастырылады. Python мүмкіндіктері C++-да қол жетімді екенін байқаған жөн, олар C++ — да пайдалануға болатын C-декларацияларында көрсетілген.
Кеңею модулі үшін қажетті барлық анықтамалар Python тақырып файлында.C/C++компиляторының тақырып файлдарының жолында болуы тиіс h. Python құрастырылған кітапханалардың сол нұсқаларын пайдалану керек. Мүмкіндігінше, C/C++компиляторының сол маркасымен.
Python интерпретаторымен C кодынан байланыс Python интерпретаторында анықталған функцияларды шақыру арқылы жүзеге асырылады. Барлық функциялар Py немесе _Py-да басталады, себебі кеңейту модулінде қақтығыстарды болдырмау үшін осындай аттармен функцияларды анықтау қажет емес.
API арқылы Python тілінің барлық кірістірілген мүмкіндіктері бар (қажет болса, бұл мәселені құжаттама бойынша егжей-тегжейлі зерттеуге болады):
— py_main (), PyRun_String (), PyRun_File (), Py_CompileString (), PyCompilerFlags () және т. б.),
— py_initialize (), Py_Finalize (), Py_NewInterpreter (), Py_EndInterpreter (), Py_SetProgramName () және басқалар),
— py_incref(), Py_DECREF(), Py_XINCREF(), Py_XDECREF(), Py_CLEAR (). C/C++кодында Python-нысандарды құру немесе жою кезінде талап етіледі.
— ерекшеліктер өңдеу (PyErr* — функциялар және pyexc_* -тұрақты, мысалы, PyErr_NoMemory () және PyExc_IOError )
— операциялық жүйенің процесін басқару және сервистері ( Py_FatalError(), Py_Exit(), Py_AtExit(), PyOS_CheckStack(), және басқа функциялар/pyos макростары* ),
— модульдерді импорттау (PyImport_Import () және басқалар),
— сериализацияны қолдау ( PyMarshal_WriteObjectToFile(), PyMarshal_ReadObjectFromFile() және т. б.)
— аргументтер жолын талдау қолдау ( PyArg_ParseTuple(), PyArg_VaParse(), PyArg_ParseTupleAndKeywords(), PyArg_VaParseTupleAndKeywords(), PyArg_UnpackTuple() және Py_BuildValue (). Бұл функциялардың көмегімен Python функциясын шақырған кезде берілген c кодында параметрлерді алу міндеті жеңілдетіледі. Pyarg_parse * функциялары алынған аргументтер пішімінің жолын дәлел ретінде қабылдайды,
— абстрактты нысандардың протоколдарын қолдау: + объект ХАТТАМАСЫ ( PyObject_Print(), PyObject_HasAttrString(), PyObject_GetAttrString(), PyObject_HasAttr(), PyObject_GetAttr(), PyObject_RichCompare ()…, PyObject_IsInstance (), Pyobject_call (), PyObject_Dir () және басқалар). Кез келген Python нысан жасау керек нәрсе + Сан ХАТТАМАСЫ ( PyNumber_Check (), PyNumber_Add (),…, PyNumber_And (),…, PyNumber_InPlaceAdd (),…, PyNumber_Coerce (), PyNumber_Int (),…). Кез келген элементті не істеу керек, Саны + бірізділік протоколы ( PySequence_Check (), PySequence_Size (), Pysequence_repeat (), PySequence_InPlaceConcat (),…, PySequence_GetItem (),…, PySequence_GetSlice (), PySequence_Tuple (), PySequence_Count ()…(Мысалы, сөздік көрініс) (функциялар: PyMapping_Check (), PyMapping_Length (), PyMapping_HasKey (), PyMapping_Keys ()…, PyMapping_SetItemString (), PyMapping_GetItemString () және т. б.) + итератордың ХАТТАМАСЫ (PyIter_Check(), Pyiter_next () + буфер ХАТТАМАСЫ (PyObject_AsCharBuffer (), PyObject_AsReadBuffer (), PyObject_AsWriteBuffer (), PyObject_CheckReadBuffer() )
— кірістірілген деректер түрлерін қолдау. Алдыңғы тармақта сипатталған ұқсас, бірақ нақты енгізілген деректер түрлері үшін. Мысалы: + Булевский объект ( PyBool_Check () — pybool_type, Py_False-false нысаны, Py_True-True нысаны,
— pymem_malloc(), PyMem_Realloc(), PyMem_Free(), PyMem_New(), PyMem_Resize(), PyMem_Del (). Әрине, C/C++ жады бөлу құралдарын да қолдануға болады, алайда бұл жағдайда Python интерпретаторы жадысын басқару артықшылықтары пайдаланылмайды (қоқыс және т.б. жинау). Сонымен қатар, жадты босату оны таңдау сияқты тәсілмен жүргізілуі керек. Тағы бір рет еске салу керек, бір жад аймағын қайта босату (сонымен қатар оны босатқаннан кейін жад аймағын пайдалану) c компиляторы тануға мүмкіндігі жоқ елеулі қателіктерге ұшырайды.
— кіріктірме типті объектілерді анықтауға арналған құрылымдар ( PyObject, PyVarObject және басқа да көптеген).

Ескертпе.
Бұл жерде хаттама деп өз даналарымен операцияларды ұйымдастыру үшін қандай да бір сыныпты қолдауы тиіс әдістер жиынтығы түсініледі. Бұл әдістер тек Python (мысалы, len(A) тізбектің ұзындығын береді) ғана емес, сонымен қатар C кодынан ( Sequence_Length () қол жетімді.


2. Кеңейту модулін жазу.

Егер Python бағдарламасын енгізу қажет болса, онда C/C++ модулін жазу арқылы оның кеңеюі — өте кең таралған тәжірибе. Бастапқыда Python кеңею мүмкіндігін көздеді, сондықтан қазіргі уақытта өте көп C / C++-кітапханалар Python байланысына ие.

Python байланыстыру, бірақ бірнеше автоматтандырылған болуы мүмкін, дегенмен бұл шығармашылық процесс. Өйткені, егер кітапхананы Python-да қарқынды пайдалану ұйғарылса, оны байлау мүмкіндігінше мұқият жасау керек. Мүмкін, байланыстыру барысында кітапхананы пайдалануды жеңілдетуге мүмкіндік беретін объектілі-бағытталған қондырма немесе басқа да сәулеттік өзгерістер жасалады.

Мысал ретінде md5 Модулінің бастапқы кодынан үзінді келтіруге болады, ол md5-дайджесті алу үшін функцияны іске асырады. Модуль иллюстрация мақсатында келтіріледі(яғни қысқартулармен). Модуль өз деректер түрін енгізеді, MD5Type, сондықтан функциялардың жүзеге асырылуын ғана емес, сонымен қатар кірістірілген типті сипаттау әдісін де көруге болады. Осы курс аясында кеңейту модульдерін бағдарламалау барлық қырлылығын оқып, ең бастысы осы сабақтың рухын түсіну. Дәріс курсы авторының пікіріне Қос слэш көрсетеді //:

// заголовочные файлы
#include «Python.h»
#include «md5.h»

// В частности, в заголовочном файле md5.h есть следующие определения:
// typedef unsigned char *POINTER;
// typedef unsigned int UINT4;

// typedef struct {
// UINT4 state[4]; /* state (ABCD) */
// UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */
// unsigned char buffer[64]; /* input buffer */
// } MD5_CTX;

// Структура объекта MD5type
typedef struct {
PyObject_HEAD
MD5_CTX md5; /* the context holder */
} md5object;

// Определение типа объекта MD5type
static PyTypeObject MD5type;

// Макрос проверки типа MD5type
#define is_md5object(v) ((v)->ob_type == &MD5type)

// Порождение объекта типа MD5type
static md5object *
newmd5object(void)
{
md5object *md5p;
md5p = PyObject_New(md5object, &MD5type);
if (md5p == NULL)
return NULL; // не хватило памяти
MD5Init(&md5p->md5); // инициализация
return md5p;
}

// Определения методов

// Освобождение памяти из-под объекта
static void
md5_dealloc(md5object *md5p) { PyObject_Del(md5p); }

static PyObject *
md5_update(md5object *self, PyObject *args)
{
unsigned char *cp;
int len;

// разбор строки аргументов. Формат указывает следующее:
// s# — один параметр, строка (заданная указателем и длиной)
// : — разделитель
// update — название метода
if (!PyArg_ParseTuple(args, «s#:update», &cp, &len))
return NULL;

MD5Update(&self->md5, cp, len);

// Даже возврат None требует увеличения счетчика ссылок
Py_INCREF(Py_None);
return Py_None;
}

// Строка документации метода update
PyDoc_STRVAR(update_doc,
«update (arg)\n\
\n\
Update the md5 object with the string arg. Repeated calls are\n\
equivalent to a single call with the concatenation of all the\n\
arguments.»);

// Метод digest
static PyObject *
md5_digest(md5object *self)
{
MD5_CTX mdContext;
unsigned char aDigest[16];

/* make a temporary copy, and perform the final */
mdContext = self->md5;
MD5Final(aDigest, &mdContext);

// результат возвращается в виде строки
return PyString_FromStringAndSize((char *)aDigest, 16);
}

// и строка документации
PyDoc_STRVAR(digest_doc, «digest() -> string\n\ …»);


static PyObject *
md5_hexdigest(md5object *self)
{
// Реализация метода на C
}

PyDoc_STRVAR(hexdigest_doc, «hexdigest() -> string\n…»);


// Здесь было определение метода copy()

// Методы объекта в сборе.
// Для каждого метода указывается название, имя метода на C
// (с приведением к типу PyCFunction), способ передачи аргументов:
// METH_VARARGS (переменное кол-во) или METH_NOARGS (нет аргументов)
// В конце массива — метка окончания спиcка аргументов.
static PyMethodDef md5_methods[] = {
{«update», (PyCFunction)md5_update, METH_VARARGS, update_doc},
{«digest», (PyCFunction)md5_digest, METH_NOARGS, digest_doc},
{«hexdigest», (PyCFunction)md5_hexdigest, METH_NOARGS, hexdigest_doc},
{«copy», (PyCFunction)md5_copy, METH_NOARGS, copy_doc},
{NULL, NULL} /* sentinel */
};

// Атрибуты md5-объекта обслуживает эта функция, реализуя метод
// getattr.
static PyObject *
md5_getattr(md5object *self, char *name)
{
// атрибут-данное digest_size
if (strcmp(name, «digest_size») == 0) {
return PyInt_FromLong(16);
}
// поиск атрибута-метода ведется в списке
return Py_FindMethod(md5_methods, (PyObject *)self, name);
}

// Строка документации к модулю md5
PyDoc_STRVAR(module_doc, «This module implements …»);

// Строка документации к классу md5
PyDoc_STRVAR(md5type_doc, «An md5 represents the object…»);

// Структура для объекта MD5type с описаниями для интерпретатора
static PyTypeObject MD5type = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
«md5.md5», /*tp_name*/
sizeof(md5object), /*tp_size*/
0, /*tp_itemsize*/
/* methods */
(destructor)md5_dealloc, /*tp_dealloc*/
0, /*tp_print*/
(getattrfunc)md5_getattr, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash*/
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
0, /*tp_xxx4*/
md5type_doc, /*tp_doc*/
};


// Функции модуля md5:

// Функция new() для получения нового объекта типа md5type
static PyObject *
MD5_new(PyObject *self, PyObject *args)
{
md5object *md5p;
unsigned char *cp = NULL;
int len = 0;

// Разбор параметров. Здесь вертикальная черта
// в строке формата означает окончание
// списка обязательных параметров.
// Остальное — как и выше: s# — строка, после : — имя
if (!PyArg_ParseTuple(args, «|s#:new», &cp, &len))
return NULL;

if ((md5p = newmd5object()) == NULL)
return NULL;

// Если был задан параметр cp:
if (cp)
MD5Update(&md5p->md5, cp, len);

return (PyObject *)md5p;
}

// Строка документации для new()
PyDoc_STRVAR(new_doc, «new([arg]) -> md5 object …»);

// Список функций, которые данный модуль экспортирует
static PyMethodDef md5_functions[] = {
{«new», (PyCFunction)MD5_new, METH_VARARGS, new_doc},
{«md5», (PyCFunction)MD5_new, METH_VARARGS, new_doc},
{NULL, NULL} /* Sentinel */
};
// Следует заметить, что md5 — то же самое, что new. Эта функция оставлена для
// обратной совместимости со старым модулем md5

// Инициализация модуля
PyMODINIT_FUNC
initmd5(void)
{
PyObject *m, *d;

MD5type.ob_type = &PyType_Type;
// Инициализируется модуль
m = Py_InitModule3(«md5», md5_functions, module_doc);
// Получается словарь с именами модуля
d = PyModule_GetDict(m);
// Добавляется атрибут MD5Type (тип md5-объекта) к словарю
PyDict_SetItemString(d, «MD5Type», (PyObject *)&MD5type);
// Добавляется целая константа digest_size к модулю
PyModule_AddIntConstant(m, «digest_size», 16);
}
Осы мысал негізінде C/API бойынша құжаттамамен және Python стандартты жеткізуден «Extending and Embedding» («кеңейту және кірістіру») құжатымен танысып, өз кеңейту модульдерін құруға болады. Өз модулін құруға кірісер алдында, бұл орынды екеніне көз жеткізу керек: қолайлы модуль әлі құрылмаған және таза Python түрінде іске асыру тиімсіз. Егер шын мәнінде пайдалы модуль жасалса, оны Python жеткізу үшін ұсынуға болады. Ол үшін электрондық пошта арқылы әзірлеушілердің біреуімен немесе «патч» түрінде модульді ұсыну қажет. http://sourceforge.net ескерту.

3. SWIG пайдалану.
SWIG (Simplified Wrapper and Interface Generator, оңайлатылған ораушы және интерфейстер генераторы) — бұл C және C++ жазылған кітапханаларды, сондай-ақ басқа да бағдарламалау тілдерінде, оның ішінде (соңғы емес!) Python. Айта кету керек, SWING 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 файлының мазмұнын көруге болады.SWIG туған c: онда 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 функциясының ішінде не болып жатқанын түсіну (). Кеңес: сіз md5 Модулінің C-кодына тағы да түсініктеме көре аласыз .
Тағы да еске салу керек, Python қарағанда, C/C++ тілінде жадыны басқару анық түрде болуы тиіс. Сондықтан түрі түрлендірілген кезде free() функциясы қосылды. Егер мұны істемесе, жад ағуы пайда болады . Функцияны бірнеше рет орындаған кезде бұл ағуларды табуға болады:
>>> import freq
>>> for i in xrange(1000000):
… dummy = freq.frequency(«ABCDEF»)
>>>
Егер freq функциясы болса .frequency () жады ағуы бар, процесс өте тез барлық жадты алады.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *