Python - programowanie rozszerzeń w C
Każdy kod, który napiszesz w dowolnym języku skompilowanym, takim jak C, C ++ lub Java, można zintegrować lub zaimportować do innego skryptu Pythona. Ten kod jest traktowany jako „rozszerzenie”.
Moduł rozszerzający Python to nic innego jak zwykła biblioteka C. Na komputerach uniksowych te biblioteki zwykle kończą się na.so(dla wspólnego obiektu). Na komputerach z systemem Windows zwykle widzisz.dll (dla biblioteki połączonej dynamicznie).
Wymagania wstępne dotyczące pisania rozszerzeń
Aby rozpocząć pisanie rozszerzenia, będziesz potrzebować plików nagłówkowych Pythona.
Na komputerach z systemem Unix zwykle wymaga to zainstalowania pakietu dla programisty, takiego jak python2.5-dev .
Użytkownicy systemu Windows otrzymują te nagłówki jako część pakietu, gdy używają binarnego instalatora języka Python.
Dodatkowo zakłada się, że masz dobrą znajomość C lub C ++, aby napisać dowolne rozszerzenie Pythona przy użyciu programowania w C.
Najpierw spójrz na rozszerzenie Pythona
Aby po raz pierwszy przyjrzeć się modułowi rozszerzenia Python, musisz zgrupować swój kod na cztery części -
Plik nagłówkowy Python.h .
Funkcje C, które chcesz ujawnić jako interfejs z Twojego modułu.
Tabela odwzorowująca nazwy twoich funkcji, które programiści Pythona widzą na funkcje C wewnątrz modułu rozszerzenia.
Funkcja inicjalizacji.
Plik nagłówkowy Python.h
Musisz dołączyć plik nagłówkowy Python.h do pliku źródłowego C, który daje dostęp do wewnętrznego interfejsu API Pythona używanego do podpięcia modułu do interpretera.
Upewnij się, że dołączasz Python.h przed innymi nagłówkami, których możesz potrzebować. Musisz przestrzegać dołączeń z funkcjami, które chcesz wywołać z Pythona.
Funkcje C.
Podpisy implementacji funkcji w języku C zawsze przyjmują jedną z trzech następujących form -
static PyObject *MyFunction( PyObject *self, PyObject *args );
static PyObject *MyFunctionWithKeywords(PyObject *self,
PyObject *args,
PyObject *kw);
static PyObject *MyFunctionWithNoArgs( PyObject *self );
Każda z poprzednich deklaracji zwraca obiekt w języku Python. Nie ma czegoś takiego jak funkcja void w Pythonie, jak w C.Jeśli nie chcesz, aby twoje funkcje zwracały wartość, zwróć odpowiednik funkcji Pythona w CNonewartość. Nagłówki Pythona definiują makro Py_RETURN_NONE, które robi to za nas.
Nazwy funkcji C mogą być dowolne, ponieważ nigdy nie są widoczne poza modułem rozszerzającym. Są zdefiniowane jako funkcja statyczna .
Twoje funkcje C zwykle są nazywane przez połączenie razem modułu Pythona i nazw funkcji, jak pokazano tutaj -
static PyObject *module_func(PyObject *self, PyObject *args) {
/* Do your stuff here. */
Py_RETURN_NONE;
}
To jest funkcja Pythona o nazwie func wewnątrz modułu modułu . Będziesz umieszczać wskaźniki do funkcji C w tabeli metod modułu, który zwykle pojawia się jako następny w kodzie źródłowym.
Tabela mapowania metod
Ta tabela metod jest prostą tablicą struktur PyMethodDef. Ta struktura wygląda mniej więcej tak -
struct PyMethodDef {
char *ml_name;
PyCFunction ml_meth;
int ml_flags;
char *ml_doc;
};
Oto opis członków tej struktury -
ml_name - To jest nazwa funkcji, jaką prezentuje interpreter języka Python, gdy jest używana w programach Pythona.
ml_meth - Musi to być adres funkcji, która ma jeden z podpisów opisanych w poprzedniej sekcji.
ml_flags - To mówi interpreterowi, którego z trzech podpisów używa ml_meth.
Ta flaga zwykle ma wartość METH_VARARGS.
Ta flaga może być OR 'bitowa OR' za pomocą METH_KEYWORDS, jeśli chcesz dopuścić argumenty słów kluczowych do swojej funkcji.
Może to również mieć wartość METH_NOARGS, która wskazuje, że nie chcesz akceptować żadnych argumentów.
ml_doc - To jest dokumentacja funkcji, która może mieć wartość NULL, jeśli nie masz ochoty go pisać.
Ta tabela musi być zakończona wartownikiem składającym się z wartości NULL i 0 dla odpowiednich członków.
Przykład
Dla wyżej zdefiniowanej funkcji mamy następującą tabelę mapowania metod -
static PyMethodDef module_methods[] = {
{ "func", (PyCFunction)module_func, METH_NOARGS, NULL },
{ NULL, NULL, 0, NULL }
};
Funkcja inicjalizacji
Ostatnią częścią modułu rozszerzającego jest funkcja inicjalizacji. Ta funkcja jest wywoływana przez interpreter języka Python podczas ładowania modułu. Wymagane jest podanie nazwy funkcjiinitModule, gdzie Module to nazwa modułu.
Funkcja inicjalizacji musi zostać wyeksportowana z biblioteki, którą będziesz budować. Nagłówki Pythona definiują PyMODINIT_FUNC tak, aby zawierały odpowiednie inkantacje, które mają się wydarzyć dla określonego środowiska, w którym kompilujemy. Wystarczy go użyć podczas definiowania funkcji.
Twoja funkcja inicjalizacji C ma ogólnie następującą ogólną strukturę -
PyMODINIT_FUNC initModule() {
Py_InitModule3(func, module_methods, "docstring...");
}
Oto opis funkcji Py_InitModule3 -
func - To jest funkcja do wyeksportowania.
module_methods - To jest nazwa tabeli odwzorowań zdefiniowana powyżej.
docstring - To jest komentarz, który chcesz umieścić w swoim rozszerzeniu.
Połączenie tego wszystkiego wygląda następująco -
#include <Python.h>
static PyObject *module_func(PyObject *self, PyObject *args) {
/* Do your stuff here. */
Py_RETURN_NONE;
}
static PyMethodDef module_methods[] = {
{ "func", (PyCFunction)module_func, METH_NOARGS, NULL },
{ NULL, NULL, 0, NULL }
};
PyMODINIT_FUNC initModule() {
Py_InitModule3(func, module_methods, "docstring...");
}
Przykład
Prosty przykład wykorzystujący wszystkie powyższe koncepcje -
#include <Python.h>
static PyObject* helloworld(PyObject* self) {
return Py_BuildValue("s", "Hello, Python extensions!!");
}
static char helloworld_docs[] =
"helloworld( ): Any message you want to put here!!\n";
static PyMethodDef helloworld_funcs[] = {
{"helloworld", (PyCFunction)helloworld,
METH_NOARGS, helloworld_docs},
{NULL}
};
void inithelloworld(void) {
Py_InitModule3("helloworld", helloworld_funcs,
"Extension module example!");
}
Tutaj funkcja Py_BuildValue służy do budowania wartości Pythona. Zapisz powyższy kod w pliku hello.c. Zobaczylibyśmy, jak skompilować i zainstalować ten moduł, aby był wywoływany ze skryptu Pythona.
Tworzenie i instalowanie rozszerzeń
Distutils opakowanie sprawia, że bardzo łatwo rozprowadzać modułów Python, zarówno czystych modułów Pythona i rozszerzeń, w sposób standardowy. Moduły są rozprowadzane w formie źródłowej oraz budowane i instalowane za pomocą skryptu instalacyjnego, zwykle nazywanego setup.py, jak poniżej.
Dla powyższego modułu musisz przygotować następujący skrypt setup.py -
from distutils.core import setup, Extension
setup(name='helloworld', version='1.0', \
ext_modules=[Extension('helloworld', ['hello.c'])])
Teraz użyj następującego polecenia, które wykonałoby wszystkie potrzebne kroki kompilacji i łączenia, z odpowiednimi poleceniami i flagami kompilatora i konsolidatora, i kopiuje wynikową bibliotekę dynamiczną do odpowiedniego katalogu -
$ python setup.py install
W systemach uniksowych najprawdopodobniej będziesz musiał uruchomić to polecenie jako root, aby mieć uprawnienia do zapisu w katalogu pakietów witryn. Zwykle nie stanowi to problemu w systemie Windows.
Importowanie rozszerzeń
Po zainstalowaniu rozszerzenia możesz zaimportować i wywołać to rozszerzenie w swoim skrypcie Python w następujący sposób -
#!/usr/bin/python
import helloworld
print helloworld.helloworld()
Dałoby to następujący wynik -
Hello, Python extensions!!
Przekazywanie parametrów funkcji
Ponieważ najprawdopodobniej będziesz chciał zdefiniować funkcje, które akceptują argumenty, możesz użyć jednego z pozostałych podpisów dla swoich funkcji C. Na przykład następująca funkcja, która przyjmuje pewną liczbę parametrów, byłaby zdefiniowana w ten sposób -
static PyObject *module_func(PyObject *self, PyObject *args) {
/* Parse args and do something interesting here. */
Py_RETURN_NONE;
}
Tablica metod zawierająca wpis dla nowej funkcji wyglądałaby następująco -
static PyMethodDef module_methods[] = {
{ "func", (PyCFunction)module_func, METH_NOARGS, NULL },
{ "func", module_func, METH_VARARGS, NULL },
{ NULL, NULL, 0, NULL }
};
Możesz użyć funkcji API PyArg_ParseTuple, aby wyodrębnić argumenty z jednego wskaźnika PyObject przekazanego do funkcji C.
Pierwszym argumentem PyArg_ParseTuple jest argument args. To jest obiekt, który będziesz analizować . Drugi argument to łańcuch formatu opisujący argumenty w oczekiwanej postaci. Każdy argument jest reprezentowany przez jeden lub więcej znaków w ciągu formatu w następujący sposób.
static PyObject *module_func(PyObject *self, PyObject *args) {
int i;
double d;
char *s;
if (!PyArg_ParseTuple(args, "ids", &i, &d, &s)) {
return NULL;
}
/* Do something interesting here. */
Py_RETURN_NONE;
}
Kompilowanie nowej wersji modułu i importowanie jej umożliwia wywołanie nowej funkcji z dowolną liczbą argumentów dowolnego typu -
module.func(1, s="three", d=2.0)
module.func(i=1, d=2.0, s="three")
module.func(s="three", d=2.0, i=1)
Prawdopodobnie możesz wymyślić jeszcze więcej odmian.
PyArg_ParseTuple Function
Oto standardowy podpis dla PyArg_ParseTuple funkcja -
int PyArg_ParseTuple(PyObject* tuple,char* format,...)
Ta funkcja zwraca 0 dla błędów i wartość różną od 0 dla sukcesu. krotka to PyObject *, który był drugim argumentem funkcji C. Tutaj format jest łańcuchem w C, który opisuje obowiązkowe i opcjonalne argumenty.
Oto lista kodów formatów dla PyArg_ParseTuple funkcja -
Kod | Typ C. | Znaczenie |
---|---|---|
do | zwęglać | Ciąg znaków Pythona o długości 1 staje się znakiem C. |
re | podwójnie | Python float staje się double C. |
fa | pływak | Python float staje się float w C. |
ja | int | Int w Pythonie staje się int C. |
l | długo | Int Python staje się długi w C. |
L | długo, długo | Int Python staje się długi w C. |
O | PyObject * | Pobiera pożyczone odwołanie do argumentu języka Python o wartości innej niż NULL. |
s | zwęglać* | Ciąg Pythona bez osadzonych wartości null w znaku C *. |
s # | char * + int | Dowolny ciąg znaków Pythona na adres i długość w języku C. |
t # | char * + int | Bufor jednosegmentowy tylko do odczytu na adres C i długość. |
u | Py_UNICODE * | Python Unicode bez osadzonych wartości null do C. |
ty # | Py_UNICODE * + int | Dowolny adres i długość w standardzie Python Unicode C. |
w # | char * + int | Odczyt / zapis buforu pojedynczego segmentu na adres i długość w C. |
z | zwęglać* | Podobnie jak s, akceptuje również None (ustawia C char * na NULL). |
z # | char * + int | Podobnie jak s #, akceptuje także None (ustawia C char * na NULL). |
(...) | zgodnie z ... | Sekwencja Pythona jest traktowana jako jeden argument na element. |
| | Następujące argumenty są opcjonalne. | |
: | Formatuj koniec, po którym następuje nazwa funkcji dla komunikatów o błędach. | |
; | Koniec formatu, po którym następuje cały tekst komunikatu o błędzie. |
Zwracane wartości
Py_BuildValue przyjmuje ciąg formatu, podobnie jak PyArg_ParseTuple . Zamiast przekazywać adresy budowanych wartości, przekazujesz wartości rzeczywiste. Oto przykład pokazujący, jak zaimplementować funkcję dodawania -
static PyObject *foo_add(PyObject *self, PyObject *args) {
int a;
int b;
if (!PyArg_ParseTuple(args, "ii", &a, &b)) {
return NULL;
}
return Py_BuildValue("i", a + b);
}
Tak by to wyglądało, gdyby zostało zaimplementowane w Pythonie -
def add(a, b):
return (a + b)
Możesz zwrócić dwie wartości ze swojej funkcji w następujący sposób, byłoby to określone za pomocą listy w Pythonie.
static PyObject *foo_add_subtract(PyObject *self, PyObject *args) {
int a;
int b;
if (!PyArg_ParseTuple(args, "ii", &a, &b)) {
return NULL;
}
return Py_BuildValue("ii", a + b, a - b);
}
Tak by to wyglądało, gdyby zostało zaimplementowane w Pythonie -
def add_subtract(a, b):
return (a + b, a - b)
Py_BuildValue Function
Oto standardowy podpis dla Py_BuildValue funkcja -
PyObject* Py_BuildValue(char* format,...)
Tutaj format to napis w C, który opisuje obiekt Pythona do zbudowania. Następujące argumenty Py_BuildValue są wartościami w języku C, na podstawie których budowany jest wynik. Wynik PyObject * jest nowym odniesieniem.
Poniższa tabela zawiera listę często używanych ciągów kodu, z których zero lub więcej jest łączonych w format ciągu.
Kod | Typ C. | Znaczenie |
---|---|---|
do | zwęglać | AC char staje się ciągiem znaków Pythona o długości 1. |
re | podwójnie | AC double staje się pływakiem Pythona. |
fa | pływak | AC float zmienia się w Python float. |
ja | int | AC int staje się intem Pythona. |
l | długo | AC long staje się intem Pythona. |
N | PyObject * | Przekazuje obiekt Pythona i kradnie odniesienie. |
O | PyObject * | Przekazuje obiekt Pythona i ZWIĘKSZA go w normalny sposób. |
O & | konwertuj + anuluj * | Arbitralna konwersja |
s | zwęglać* | Znak * zakończony znakiem C 0 na łańcuch w języku Python lub NULL na brak. |
s # | char * + int | C char * i długość jako napis w języku Python lub NULL na brak. |
u | Py_UNICODE * | C-wide, zakończony znakiem null ciąg do Python Unicode lub NULL do None. |
ty # | Py_UNICODE * + int | C-szeroki ciąg i długość do Python Unicode lub NULL do None. |
w # | char * + int | Odczyt / zapis buforu pojedynczego segmentu na adres i długość w C. |
z | zwęglać* | Podobnie jak s, akceptuje również None (ustawia C char * na NULL). |
z # | char * + int | Podobnie jak s #, akceptuje także None (ustawia C char * na NULL). |
(...) | zgodnie z ... | Tworzy krotkę Pythona z wartości w C. |
[…] | zgodnie z ... | Tworzy listę Pythona z wartości C. |
{...} | zgodnie z ... | Tworzy słownik Pythona z wartości C, naprzemiennych kluczy i wartości. |
Kod {...} buduje słowniki z parzystej liczby wartości w C, na przemian kluczy i wartości. Na przykład Py_BuildValue ("{issi}", 23, "zig", "zag", 42) zwraca słownik, taki jak Python's {23: 'zig', 'zag': 42}.