Einführung


... [ Seminar WS2009/2010 - Programmiersprachen und -Systeme ] ... [ Python erweitern und einbetten - wie Python mit C interagiert ] ... [ Grundsätzliche Konzepte ] ...

Übersicht: Einführung


Was ist Erweitern / Einbetten ?

Beim Erweitern von Python geht es um die Entwicklung zusätzlicher Module zur späteren Benutzung in Python. Einige Probleme lassen sich durch Erweiterungsmodule, die selbst in Python geschrieben sind möglicherweise nicht mit der geforderten Performance realisieren. In diesen Fällen kann es sinnvoll sein, diese zeitkritischen Abläufe in ein Modul auszulagern, das in einer kompilierten und maschinennahen Sprache wie C entwickelt wurde. Man erhält dadurch ein besseres Laufzeitverhalten, nimmt allerdings in Kauf, dass der Entwicklungsaufwand i.d.R. ansteigt.

Eine in C entwickelte Erweiterung wird im Normalfall als gemeinsam nutzbare Bibliothek (shared library / shared object) erstellt und während des Ablaufs eines Python-Programms importiert. Alternativ besteht die Möglichkeit des statischen Bindens, indem man die eigene(n) Quelldatei(en) zusammen mit den Python-Quelldateien neu kompiliert, um das eigene Modul fest in den Interpreter zu integrieren. Diese Technik sollte allerdings nur in Ausnahmefällen angewandt werden und nur dann, wenn die Möglichkeit des dynamischen Bindens nicht zur Verfügung steht. Dies kann auf einigen speziellen Plattformen der Fall sein.

Wird ein Modul als Bibliothek erstellt, kann es wie gewohnt importiert werden.

Das Einbetten befasst sich mit der Verwendung von Python-Quellcode und dem Python-Interpreter innerhalb von C. In einigen Fällen kann es sehr sinnvoll sein Python-Funktionalität zu verwenden, anstatt diese Funktionalität in C zu implementieren. Beispiele und Anwedungsfälle finden sich im entsprechenden Kapitel.


Probleme

Um die Zusammenarbeit von Python und C zu ermöglichen, sind einige grundlegende Aspekte zu betrachten. Zunächst einmal sollen zwei Sprachen kombiniert werden, die unterschiedlichen Sprachparadigmen folgen. C ist eine rein imperative Sprache ohne ein objektorientiertes Konzept. Python ist eine objektorientierte Sprache mit funktionalen Anteilen. Daraus ergeben sich zwangsläufig Unterschiede in den Datentypen, die es zu überbrücken gilt. Zusätzlich werden in Python Ausnahmen unterstützt, wie man sie z.B. auch aus der Sprache Java kennt. Das Konzept der Ausnahmebehandlung ist in C nicht vorhanden. Ein weiterer sehr wesentlicher Aspekt ist die unterschiedliche Speicherverwaltung. In C ist der Programmierer alleine dafür verantwortlich dynamischen Speicher zu allokieren und diesen wieder frei zu geben. Der Python-Entwickler hingegen muss sich um die Speicherverwaltung keine Sorgen machen, da die Sprache ein Speicherverwaltungskonzept zur Verfügung stellt. Für die Überbrückung dieser und weiterer Sprachunterschiede müssen Lösungen angeboten werden. Die Schnittstelle, die es erlaubt diese Brücke zu bauen, ist das Headerfile "Python.h" mit dessen Beschreibung; der Python/C-API-Reference.

Wie die wesentlichen, hier genannten Probleme zu lösen sind, wird im Kapitel Grundsätzliche Konzepte behandelt.


Die Schnittstelle "Python.h"

Das Headerfile "Python.h" beinhaltet alles was zur Nutzung der Python/C-API notwendig ist. Innerhalb dieses Headerfiles werden weitere Schnittstellen eingebunden. Darunter auch einige C-Standard-Header. Außerdem enthält die Schnittstelle einige Präprozessor-Definitionen, die sich auf C-Standard-Header auswirken, so dass unbedingt darauf zu achten ist die Datei "Python.h" als erstes zu inkludieren. Da Funktionen und Makros der Schnittstelle immer den Präfix "PY" oder "Py" tragen, sollte es vermieden werden, die eigenen Bezeichner gleichartig beginnen zu lassen. Somit wird die Gefahr von Namenskonflikten auch mit zukünftigen Versionen vermieden.


Der Datentyp "PyObject"

Wollen wir in C mit Python-Objekten arbeiten, so verwenden wir stets Variablen des Typs Zeiger auf PyObject. Die direkte Deklaration einer Variable/Konstante vom Type PyObject ist nicht möglich. Werfen wir einen Blick auf die grundsätzliche Struktur eines Objektes in Python:
typedef struct _object {

	_PyObject_HEAD_EXTRA         /* Debugging */
	Py_ssize_t ob_refcnt;        /* aktueller Reference-Count */
	struct _typeobject *ob_type; /* Zeiger auf Datentyp */

} PyObject;
Das Element ob_refcnt vom Typ Py_ssize_t gibt an wie viele Referenzen aktuell auf das entsprechende Objekt gehalten werden (siehe: Reference Counting). Des Weiteren enthält jedes Objekt einen Zeiger auf eine Struktur, die dessen Datentyp bestimmt (siehe: Der Datentyp "PyTypeObject").


Der Datentyp "PyTypeObject"

Die Struktur definiert einen Python-Datentyp bzw. eine Python-Klasse. Innerhalb dieser Struktur werden hauptsächlich Funktionszeiger verankert. Diese Funktionen sind u.a. Konstruktor-/Destruktor, Hash-, Ausgaberepräsentations- und Vergleichsfunktionen. Im Kapitel Extending Python / Definition des Datentypen cStack wird auf spezifische Felder der Struktur näher eingegangen. Hier zunächst eine vollständige Abbildung der entsprechenden Struktur:
typedef struct _typeobject {

	PyObject_VAR_HEAD
	const char *tp_name; /* For printing, in format "<module>.<name>" */

	Py_ssize_t tp_basicsize, tp_itemsize; /* For allocation */

	/* Methods to implement standard operations */

	destructor tp_dealloc;
	printfunc tp_print;

	getattrfunc tp_getattr;
	setattrfunc tp_setattr;
	void *tp_reserved; /* formerly known as tp_compare */

	reprfunc tp_repr;

	/* Method suites for standard classes */

	PyNumberMethods *tp_as_number;
	PySequenceMethods *tp_as_sequence;

	PyMappingMethods *tp_as_mapping;

	/* More standard operations (here for binary compatibility) */

	hashfunc tp_hash;
	ternaryfunc tp_call;

	reprfunc tp_str;
	getattrofunc tp_getattro;
	setattrofunc tp_setattro;

	/* Functions to access object as input/output buffer */
	PyBufferProcs *tp_as_buffer;

	/* Flags to define presence of optional/expanded features */
	long tp_flags;

	const char *tp_doc; /* Documentation string */

	/* Assigned meaning in release 2.0 */
	/* call function for all accessible objects */

	traverseproc tp_traverse;

	/* delete references to contained objects */
	inquiry tp_clear;

	/* Assigned meaning in release 2.1 */
	/* rich comparisons */
	richcmpfunc tp_richcompare;

	/* weak reference enabler */
	Py_ssize_t tp_weaklistoffset;

	/* Iterators */
	getiterfunc tp_iter;
	iternextfunc tp_iternext;

	/* Attribute descriptor and subclassing stuff */
	struct PyMethodDef *tp_methods;
	struct PyMemberDef *tp_members;

	struct PyGetSetDef *tp_getset;
	struct _typeobject *tp_base;

	PyObject *tp_dict;
	descrgetfunc tp_descr_get;
	descrsetfunc tp_descr_set;
	Py_ssize_t tp_dictoffset;

	initproc tp_init;
	allocfunc tp_alloc;
	newfunc tp_new;
	freefunc tp_free; /* Low-level free-memory routine */

	inquiry tp_is_gc; /* For PyObject_IS_GC */
	PyObject *tp_bases;
	PyObject *tp_mro; /* method resolution order */

	PyObject *tp_cache;
	PyObject *tp_subclasses;
	PyObject *tp_weaklist;

	destructor tp_del;

	/* Type attribute cache version tag. Added in version 2.6 */
	unsigned int tp_version_tag;

#ifdef COUNT_ALLOCS
	/* these must be last and never explicitly initialized */

	Py_ssize_t tp_allocs;
	Py_ssize_t tp_frees;
	Py_ssize_t tp_maxalloc;
	struct _typeobject *tp_prev;

	struct _typeobject *tp_next;
#endif
} PyTypeObject;


... [ Seminar WS2009/2010 - Programmiersprachen und -Systeme ] ... [ Python erweitern und einbetten - wie Python mit C interagiert ] ... [ Grundsätzliche Konzepte ] ...