L | F | D | W

Python C-Module


Um Python mit C-Code zu verwenden, muss das vorhandene *.c-File um ein paar spezifische Codeteile ergänzt werden. Da das sehr ausführlich in den Python-Dokumentionen beschrieben wird, gehe ich nur auf die wesentlichen Teile ein.
Drei einfache Bespiele, möchte ich hier zeigen.

  1. Parameterübergabe von einem String-Wert.
  2. Parameterübergabe von zwei String-Werten.
  3. Parameterübergabe von einem Integer-Wert.

Alle drei Beispiele folgen demselben Schema. Einen, das für mich funktioniert. Jedoch sind noch sehr viel komplexere Module möglich, und dazu ist jedoch einiges an Verständnis zur C- und Pythonprogrammierung erforderlich.

1. Parameterübergabe von einem String

In diesem Beispiel wird ein Filename übergeben und die entsprechende Inodenummer zurückgegeben. Dafür wird eine C-Klasse sys/stat.h im C-Module verwendet, die nur für Linux zur Verfügung steht.

Hier einmal der Code.

#include <sys/types.h>
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <sys/stat.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>

int
CStatus(const char *n)
{
struct stat sb;
stat(n, &sb);

//printf("Inode %ld\n", sb.st_ino);
return (long) sb.st_ino;
}
static PyObject *
stat_num(PyObject *self, PyObject *args) {
const char *n;
if(!PyArg_ParseTuple(args, "s", &n))
{
return NULL;
}

return Py_BuildValue("i", CStatus(n));
//Py_RETURN_NONE;
}
static PyMethodDef stat_methods[] = {
{
"status", stat_num, METH_VARARGS, "Inodenummer ermitteln."
},
{ NULL, NULL, 0, NULL}
};
static struct PyModuleDef status =
{
PyModuleDef_HEAD_INIT,
"status", /* name of module */
"", /* module documentation, may be NULL */
-1, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */
stat_methods
};
PyMODINIT_FUNC PyInit_status(void) {
Py_Initialize();
return PyModule_Create(&status);
}


Den Code kann man grundsätzlich in drei Teile aufgliedern.

Oben ist der individuelle Teil. Also der selbst geschriebene Code. Die Funktion CStatus soll die Variable "n", den Filenamen, übernehmen und die Inodenummer returnieren. Dazu wird die Klasse sys/stat.h geladen.

int
CStatus(const char *n)
{
struct stat sb;
stat(n, &sb);

//printf("Inode %ld\n", sb.st_ino);
return (long) sb.st_ino;
}


Zunächst muss die Python_API geladen werden. Die erste Zeile ist eine Makro, dass immer vor der eigentlichen API geladen werden muss.

#define PY_SSIZE_T_CLEAN
#include <Python.h>


Als Nächstes laden wir mit einer C-Funktion unseren Input. Dazu wird die Funktion "stat_num" definiert. Zu beachten ist die Definition der Variablen "n" und dem Retournieren unserer Funktion CStatus mit der Variablen n als Input. Hier übergeben wir dem C-Code unsere Variable "n" und geben die Inodenummer an Python zurück.
Dabei ist dieses Statement zu beachten, dass "NULL" zurückgegeben wird, wenn ein Fehler auftritt, sonst wird das Argument in den lokalen Wert "n" kopiert. "s" ist dabei ein String, also unser Input-Typwert.

if (!PyArg_ParseTuple(args, "s", &command))
return NULL;


Als Ergänzung kann auch kein Rückgabewert weitergeleitet werden. Man kann dann seine Funktionen direkt im C-Code ausführen lassen. Dazu wird die Zeile "Py_RETURN_NONE" verwendet.

static PyObject *
stat_num(PyObject *self, PyObject *args) {
const char *n;
if(!PyArg_ParseTuple(args, "s", &n))
{
return NULL;
}

return Py_BuildValue("i", CStatus(n));
//Py_RETURN_NONE;
}


Der nächste Teil ist eine Methodentable-Definition in der die Funktion "stat_num" registriert wird.

static PyMethodDef stat_methods[] = {
{
"status", stat_num, METH_VARARGS, "Inodenummer ermitteln."
},
{ NULL, NULL, 0, NULL}
};


Die Methodentable wird nun in der Methoden-Definition registriert.

static struct PyModuleDef status =
{
PyModuleDef_HEAD_INIT,
"status", /* name of module */
"", /* module documentation, may be NULL */
-1, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */
stat_methods
};


Diese Struktur wird nun an den Interpreter übergeben.

PyMODINIT_FUNC PyInit_status(void) {
Py_Initialize();
return PyModule_Create(&status);


Damit hätten wir eine C-Funktion, die wir kompilieren können, und von Python verwendet werden kann. Nähere Informationen dazu sind in der offiziellen Dokumentation zu finden.

Python 3 Dokumention zur externen Modulen

Um jetzt eine einfache Möglichkeit zum Kompilieren zu verwenden, eignet es sich ein Paket (Wheel) daraus zu bauen. Dazu habe ich einen Artikel geschrieben, der sich auch genau auf Pakete mit C-Modulen bezieht.

Pakete erstellen und installieren

2. Parameterübergabe von zwei String-Werten

Dazu erweitern wir den selbstgeschrieben Code und die Python-Funktion um die entsprechenden Defintionen. Hier wird eine weitere Variable "k" definiert. Und zum Unterschied zum vorherigen Beispiel wird direkt im C-Code die Variable verwendet und kein Rückgabewert weitergegeben.
An zwei Stellen ist ein Prinf-Statement, dass die Variable anzeigt, direkt im C-Code.

int
CStatus(const char *n, const char *k)
{
struct stat sb;
stat(n, &sb);

//printf("Inode %ld\n", sb.st_ino);
printf("Variable K in der Custom-Funktion %s\n", k);
return (long) sb.st_ino;
}
static PyObject *
stat_num(PyObject *self, PyObject *args) {
const char *n;
const char *k;
if(!PyArg_ParseTuple(args, "ss", &n, &k))
{
return NULL;
}

printf("Variable K in der Python-Funktion %s\n", k);
//return Py_BuildValue("i", CStatus(n,k));
Py_RETURN_NONE;
}


Um den Code nun auszuführen hier eine Beispiel.

Python 3.11.0rc2 (main, Sep 13 2022, 00:00:00) [GCC 12.2.1 20220819 (Red Hat 12.2.1-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import status

>>> print("Inodenumber {}".format(status.status("/home/fips/inode2.c","ein Text")))

>>> Variable K in der Custom-Funktion, ein Text
>>> Variable K in der Python-Funktion, ein Text
>>> Inodenumber 65777777


3. Parameterübergabe von einem Integer-Wert

In diesem Beispiel wird ein Integerwert übergeben und auch retourniert. Es wird ein Wert der Fibonacci-Reihe berechnet und zurückgegeben.

#define PY_SSIZE_T_CLEAN
#include <Python.h>

long long
CFibonacci(long long m){
if(m < 2)
return m;
else
return CFibonacci(m - 1) + CFibonacci(m - 2);
}
// this function will be binding our python version and our C version together
// will only take one and only one non-keyword arguement
static PyObject *
fib_num(PyObject *self, PyObject *args) {
long long m;
if(!PyArg_ParseTuple(args, "i", &m))
return NULL;
return Py_BuildValue("i", CFibonacci(m));
}
static PyMethodDef fibonacci_methods[] = {
{
"fibonacci", fib_num, METH_VARARGS, "Berechne fibonacci"
},
{ NULL, NULL, 0, NULL}
};
static struct PyModuleDef fibonacci_def =
{
PyModuleDef_HEAD_INIT,
"fibonacci", /* name of module */
"", /* module documentation, may be NULL */
-1, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */
fibonacci_methods
};
PyMODINIT_FUNC PyInit_fibonacci(void) {
Py_Initialize();
return PyModule_Create(&fibonacci_def);


Ein Unterschied besteht in den Funktionen CFibonacci und fib_num, in denen statt eines Char/String-Wert nun ein Integerwert/(Long Long) definiert werden muss.
Hier ein Beispiel zur Anwendung.

Python 3.11.0rc2 (main, Sep 13 2022, 00:00:00) [GCC 12.2.1 20220819 (Red Hat 12.2.1-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import fib_fibonacci.fibo
>>> import fibonacci

>>> print(fibonacci.fibonacci(12))
>>> print(fib_fibonacci.fibo.hallo())

>>> 144
>>> Hallo


Weitere Quellen und Suchbegriffe: Python, C-Module, Python-Module



geändert, 18. Nov 2022, 23:52
Copyright © 2025 by Philipp