Python - Extensie programmeren met C
Elke code die u schrijft met behulp van een gecompileerde taal zoals C, C++ of Java, kan worden geïntegreerd of geïmporteerd in een ander Python-script. Deze code wordt beschouwd als een "extensie".
Een Python-uitbreidingsmodule is niets meer dan een normale C-bibliotheek. Op Unix-machines eindigen deze bibliotheken meestal op .so (voor gedeeld object). Op Windows-machines ziet u meestal .dll (voor dynamisch gekoppelde bibliotheek).
Vereisten voor het schrijven van extensies
Om te beginnen met het schrijven van je extensie, heb je de Python-headerbestanden nodig.
-
Op Unix-machines moet hiervoor meestal een ontwikkelaarspecifiek pakket worden geïnstalleerd, zoals python2.5-dev.
-
Windows-gebruikers krijgen deze headers als onderdeel van het pakket wanneer ze het binaire Python-installatieprogramma gebruiken.
Bovendien wordt aangenomen dat je een goede kennis hebt van C of C++ om een Python-extensie te schrijven met C-programmering.
Eerst kijken naar een Python-extensie
Voor uw eerste blik op een Python-uitbreidingsmodule, moet u uw code in vier delen groeperen −
-
Het headerbestand Python.h .
-
De C-functies die u wilt weergeven als de interface van uw module.
-
Een tabel die de namen van je functies in kaart brengt zoals Python-ontwikkelaars ze zien aan C-functies in de uitbreidingsmodule.
-
Een initialisatiefunctie.
Het kopbestand Python.h
U moet Python.h . opnemen header-bestand in uw C-bronbestand, dat u toegang geeft tot de interne Python API die wordt gebruikt om uw module in de interpreter te haken.
Zorg ervoor dat u Python.h opneemt vóór andere headers die u mogelijk nodig hebt. Je moet de include volgen met de functies die je wilt aanroepen vanuit Python.
De C-functies
De handtekeningen van de C-implementatie van uw functies nemen altijd een van de volgende drie vormen aan −
static PyObject *MyFunction( PyObject *self, PyObject *args ); static PyObject *MyFunctionWithKeywords(PyObject *self, PyObject *args, PyObject *kw); static PyObject *MyFunctionWithNoArgs( PyObject *self );
Elk van de voorgaande declaraties retourneert een Python-object. Er bestaat niet zoiets als een leegte functie in Python zoals er is in C. Als u niet wilt dat uw functies een waarde retourneren, retourneert u het C-equivalent van Python's Geen waarde. De Python-headers definiëren een macro, Py_RETURN_NONE, die dit voor ons doet.
De namen van uw C-functies kunnen zijn wat u maar wilt, aangezien ze nooit buiten de uitbreidingsmodule worden gezien. Ze zijn gedefinieerd als statisch functie.
Uw C-functies worden meestal benoemd door de Python-module en functienamen samen te combineren, zoals hier getoond −
static PyObject *module_func(PyObject *self, PyObject *args) { /* Do your stuff here. */ Py_RETURN_NONE; }
Dit is een Python-functie genaamd func binnenkant van de module module . Je plaatst verwijzingen naar je C-functies in de methodetabel voor de module die meestal de volgende is in je broncode.
De methodetoewijzingstabel
Deze methodetabel is een eenvoudige reeks PyMethodDef-structuren. Die structuur ziet er ongeveer zo uit −
struct PyMethodDef { char *ml_name; PyCFunction ml_meth; int ml_flags; char *ml_doc; };
Hier is de beschrijving van de leden van deze structuur −
-
ml_name − Dit is de naam van de functie zoals de Python-interpreter deze presenteert wanneer deze wordt gebruikt in Python-programma's.
-
ml_meth − Dit moet het adres zijn van een functie die een van de handtekeningen heeft die in de vorige paragraaf zijn beschreven.
-
ml_flags − Dit vertelt de interpreter welke van de drie handtekeningen ml_meth gebruikt.
-
Deze vlag heeft meestal een waarde van METH_VARARGS.
-
Deze vlag kan bitsgewijze OR'ed worden met METH_KEYWORDS als u trefwoordargumenten in uw functie wilt toestaan.
-
Dit kan ook de waarde METH_NOARGS hebben die aangeeft dat u geen argumenten wilt accepteren.
-
-
ml_doc − Dit is de docstring voor de functie, die NULL kan zijn als je geen zin hebt om er een te schrijven.
Deze tabel moet worden afgesloten met een schildwacht die bestaat uit NULL- en 0-waarden voor de juiste leden.
Voorbeeld
Voor de hierboven gedefinieerde functie hebben we de volgende methodetoewijzingstabel −
static PyMethodDef module_methods[] = { { "func", (PyCFunction)module_func, METH_NOARGS, NULL }, { NULL, NULL, 0, NULL } };
De initialisatiefunctie
Het laatste onderdeel van uw uitbreidingsmodule is de initialisatiefunctie. Deze functie wordt aangeroepen door de Python-interpreter wanneer de module wordt geladen. De functie moet de naam initModule . hebben , waarbij Module is de naam van de module.
De initialisatiefunctie moet worden geëxporteerd vanuit de bibliotheek die u gaat bouwen. De Python-headers definiëren PyMODINIT_FUNC om de juiste bezweringen op te nemen om dat te laten gebeuren voor de specifieke omgeving waarin we compileren. Het enige wat u hoeft te doen is het te gebruiken bij het definiëren van de functie.
Uw C-initialisatiefunctie heeft over het algemeen de volgende algemene structuur −
PyMODINIT_FUNC initModule() { Py_InitModule3(func, module_methods, "docstring..."); }
Hier is de beschrijving van Py_InitModule3 functie −
-
leuk − Dit is de functie die moet worden geëxporteerd.
-
module _methoden − Dit is de naam van de toewijzingstabel die hierboven is gedefinieerd.
-
docstring − Dit is de opmerking die u in uw extensie wilt geven.
Als je dit allemaal samenvoegt, ziet het er als volgt uit −
#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..."); }
Voorbeeld
Een eenvoudig voorbeeld dat gebruik maakt van alle bovenstaande concepten −
#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!"); }
Hier de Py_BuildValue functie wordt gebruikt om een Python-waarde te bouwen. Bewaar bovenstaande code in hello.c bestand. We zouden zien hoe we deze module kunnen compileren en installeren om te worden aangeroepen vanuit het Python-script.
Extensies bouwen en installeren
De distutils pakket maakt het zeer eenvoudig om Python-modules, zowel pure Python- als uitbreidingsmodules, op een standaard manier te distribueren. Modules worden gedistribueerd in bronvorm en gebouwd en geïnstalleerd via een installatiescript dat gewoonlijk setup.py wordt genoemd als volgt.
Voor de bovenstaande module moet u het volgende setup.py-script voorbereiden −
from distutils.core import setup, Extension setup(name='helloworld', version='1.0', \ ext_modules=[Extension('helloworld', ['hello.c'])])
Gebruik nu de volgende opdracht, die alle benodigde compilatie- en koppelingsstappen uitvoert, met de juiste compiler- en linkeropdrachten en vlaggen, en kopieert de resulterende dynamische bibliotheek naar een geschikte map −
$ python setup.py install
Op Unix-gebaseerde systemen moet u deze opdracht waarschijnlijk als root uitvoeren om machtigingen te hebben om naar de map site-packages te schrijven. Dit is meestal geen probleem op Windows.
Extensies importeren
Nadat u uw extensie hebt geïnstalleerd, kunt u die extensie als volgt importeren en aanroepen in uw Python-script −
#!/usr/bin/python import helloworld print helloworld.helloworld()
Dit zou het volgende resultaat opleveren −
Hello, Python extensions!!
Functieparameters doorgeven
Aangezien u hoogstwaarschijnlijk functies wilt definiëren die argumenten accepteren, kunt u een van de andere handtekeningen voor uw C-functies gebruiken. De volgende functie, die een aantal parameters accepteert, zou bijvoorbeeld als volgt worden gedefinieerd −
static PyObject *module_func(PyObject *self, PyObject *args) { /* Parse args and do something interesting here. */ Py_RETURN_NONE; }
De methodetabel die een invoer voor de nieuwe functie bevat, ziet er als volgt uit −
static PyMethodDef module_methods[] = { { "func", (PyCFunction)module_func, METH_NOARGS, NULL }, { "func", module_func, METH_VARARGS, NULL }, { NULL, NULL, 0, NULL } };
U kunt API PyArg_ParseTuple . gebruiken functie om de argumenten te extraheren uit de ene PyObject-aanwijzer die is doorgegeven aan uw C-functie.
Het eerste argument voor PyArg_ParseTuple is het argument args. Dit is het object dat u gaat parseren . Het tweede argument is een opmaakreeks die de argumenten beschrijft zoals u verwacht dat ze verschijnen. Elk argument wordt als volgt weergegeven door een of meer tekens in de opmaakreeks.
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; }
Door de nieuwe versie van uw module te compileren en te importeren, kunt u de nieuwe functie aanroepen met een willekeurig aantal argumenten van elk type −
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)
Je kunt waarschijnlijk nog meer variaties bedenken.
De PyArg_ParseTuple Functie
Hier is de standaard handtekening voor PyArg_ParseTuple functie −
int PyArg_ParseTuple(PyObject* tuple,char* format,...)
Deze functie retourneert 0 voor fouten en een waarde die niet gelijk is aan 0 voor succes. tuple is het PyObject* dat het tweede argument van de C-functie was. Hier opmaak is een C-tekenreeks die verplichte en optionele argumenten beschrijft.
Hier is een lijst met opmaakcodes voor PyArg_ParseTuple functie −
Code | C-type | Betekenis |
---|---|---|
c | char | Een Python-tekenreeks met lengte 1 wordt een C-teken. |
d | dubbel | Een Python-float wordt een C-double. |
f | drijvend | Een Python-float wordt een C-float. |
ik | int | Een Python int wordt een C int. |
l | lang | Een Python int wordt een C lang. |
L | lang lang | Een Python int wordt een C lang lang |
O | PyObject* | Krijgt een niet-NULL geleende verwijzing naar het Python-argument. |
s | char* | Python string zonder ingesloten nulls naar C char*. |
s# | char*+int | Elke Python-string naar C-adres en lengte. |
t# | char*+int | Alleen-lezen buffer met één segment naar C-adres en lengte. |
u | Py_UNICODE* | Python Unicode zonder ingesloten nulls naar C. |
u# | Py_UNICODE*+int | Elk Python Unicode C-adres en lengte. |
w# | char*+int | Lees/schrijf enkel-segment buffer naar C adres en lengte. |
z | char* | Like s, accepteert ook Geen (zet C char* op NULL). |
z# | char*+int | Net als s#, accepteert ook Geen (zet C char* op NULL). |
(...) | volgens ... | Een Python-reeks wordt behandeld als één argument per item. |
| | De volgende argumenten zijn optioneel. | |
: | Formaat einde, gevolgd door functienaam voor foutmeldingen. | |
; | Formaat einde, gevolgd door de volledige tekst van het foutbericht. |
Waarden retourneren
Py_BuildValue neemt een formaattekenreeks in zoals PyArg_ParseTuple doet. In plaats van de adressen door te geven van de waarden die u aan het bouwen bent, geeft u de werkelijke waarden door. Hier is een voorbeeld dat laat zien hoe je een add-functie implementeert −
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); }
Dit is hoe het eruit zou zien als het geïmplementeerd was in Python −
def add(a, b): return (a + b)
U kunt als volgt twee waarden uit uw functie retourneren, dit zou worden vastgelegd met behulp van een lijst in Python.
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); }
Dit is hoe het eruit zou zien als het geïmplementeerd was in Python −
def add_subtract(a, b): return (a + b, a - b)
De Py_BuildValue Functie
Hier is de standaard handtekening voor Py_BuildValue functie −
PyObject* Py_BuildValue(char* format,...)
Hier opmaak is een C-tekenreeks die het te bouwen Python-object beschrijft. De volgende argumenten van Py_BuildValue zijn C-waarden waaruit het resultaat is opgebouwd. Het PyObject* resultaat is een nieuwe referentie.
De volgende tabel geeft een overzicht van de veelgebruikte codereeksen, waarvan nul of meer worden samengevoegd in tekenreeksindeling.
Code | C-type | Betekenis |
---|---|---|
c | char | Een C-teken wordt een Python-tekenreeks met lengte 1. |
d | dubbel | Een C-double wordt een Python-float. |
f | drijvend | Een C-float wordt een Python-float. |
ik | int | Een C int wordt een Python int. |
l | lang | Een C long wordt een Python int. |
N | PyObject* | Geeft een Python-object door en steelt een referentie. |
O | PyObject* | Geeft een Python-object door en INCREFs het zoals normaal. |
O& | convert+void* | Willekeurige conversie |
s | char* | C 0-terminated char* naar Python string, of NULL naar None. |
s# | char*+int | C char* en lengte naar Python-tekenreeks, of NULL naar Geen. |
u | Py_UNICODE* | C-brede, null-terminated string naar Python Unicode, of NULL naar None. |
u# | Py_UNICODE*+int | C-brede string en lengte naar Python Unicode, of NULL naar Geen. |
w# | char*+int | Lees/schrijf enkel-segment buffer naar C adres en lengte. |
z | char* | Like s, accepteert ook Geen (zet C char* op NULL). |
z# | char*+int | Net als s#, accepteert ook Geen (zet C char* op NULL). |
(...) | volgens ... | Bouwt Python-tuple van C-waarden. |
[...] | volgens ... | Bouwt een Python-lijst op van C-waarden. |
{...} | volgens ... | Bouwt Python-woordenboek op basis van C-waarden, afwisselende sleutels en waarden. |
Code {...} bouwt woordenboeken op van een even aantal C-waarden, afwisselend sleutels en waarden. Py_BuildValue("{issi}",23,"zig","zag",42) retourneert bijvoorbeeld een woordenboek zoals {23:'zig','zag':42} van Python.
Python
- Objectgeoriënteerd programmeren in Python
- Python Print()-instructie:afdrukken met voorbeelden
- Python String strip() Functie met VOORBEELD
- Python String count() met VOORBEELDEN
- Python String format() Leg uit met VOORBEELDEN
- Methode Python String find() met voorbeelden
- Python Lambda-functies met VOORBEELDEN
- Python round() functie met VOORBEELDEN
- Python map() functie met VOORBEELDEN
- Python Timeit() met voorbeelden
- Python-teller in verzamelingen met voorbeeld