Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
1 Einleitung
2 Überblick über Python
3 Die Arbeit mit Python
4 Der interaktive Modus
5 Grundlegendes zu Python-Programmen
6 Kontrollstrukturen
7 Das Laufzeitmodell
8 Basisdatentypen
9 Benutzerinteraktion und Dateizugriff
10 Funktionen
11 Modularisierung
12 Objektorientierung
13 Weitere Spracheigenschaften
14 Mathematik
15 Strings
16 Datum und Zeit
17 Schnittstelle zum Betriebssystem
18 Parallele Programmierung
19 Datenspeicherung
20 Netzwerkkommunikation
21 Debugging
22 Distribution von Python-Projekten
23 Optimierung
24 Grafische Benutzeroberflächen
25 Python als serverseitige Programmiersprache im WWW mit Django
26 Anbindung an andere Programmiersprachen
27 Insiderwissen
28 Zukunft von Python
A Anhang
Stichwort

Download:
- ZIP, ca. 4,8 MB
Buch bestellen
Ihre Meinung?

Spacer
 <<   zurück
Python von Peter Kaiser, Johannes Ernesti
Das umfassende Handbuch - Aktuell zu Python 2.5
Buch: Python

Python
gebunden, mit CD
819 S., 39,90 Euro
Galileo Computing
ISBN 978-3-8362-1110-9
Pfeil 26 Anbindung an andere Programmiersprachen
  Pfeil 26.1 Dynamisch ladbare Bibliotheken – ctypes
    Pfeil 26.1.1 Ein einfaches Beispiel
    Pfeil 26.1.2 Die eigene Bibliothek
    Pfeil 26.1.3 Schnittstellenbeschreibung
    Pfeil 26.1.4 Verwendung des Moduls
  Pfeil 26.2 Schreiben von Extensions
    Pfeil 26.2.1 Ein einfaches Beispiel
    Pfeil 26.2.2 Exceptions
    Pfeil 26.2.3 Erzeugen der Extension
    Pfeil 26.2.4 Reference Counting
  Pfeil 26.3 Python als eingebettete Skriptsprache
    Pfeil 26.3.1 Ein einfaches Beispiel
    Pfeil 26.3.2 Ein komplexeres Beispiel
    Pfeil 26.3.3 Python-API-Referenz


Galileo Computing - Zum Seitenanfang

26.2 Schreiben von Extensions  Zur nächsten ÜberschriftZur vorigen Überschrift

Im letzten Kapitel haben wir uns mit einem Ansatz beschäftigt, bestimmte Teile eines Programms in C zu implementieren, in eine dynamische Bibliothek auszulagern und dann aus dem Python-Programm heraus anzusprechen. Das ist, wie Sie sich sicherlich vorstellen können, für eine Anwendung eine tolle Sache, doch gerade beim Schreiben eines Moduls wäre der ctypes-Ansatz aufgrund seiner Einschränkungen an der Schnittstelle nicht besonders praktikabel.

Wir sind im Laufe des Buchs bereits öfters auf Bibliotheken wie cStringIO oder cProfile gestoßen, bei denen gesagt wurde, dass sie aus Effizienzgründen in C geschrieben wurden. Offensichtlich ist es also möglich und tatsächlich auch durchaus üblich, ein Python-Modul in reinem C zu schreiben und dann ohne ctypes zu verwenden. Solche Module werden Extentions (dt. Erweiterungen) genannt.

Solche Extensions werden mithilfe der sogenannten Python API geschrieben. Mit dieser API ist es möglich, in C beispielsweise Instanzen eines Python-Datentyps zu erzeugen und zu verarbeiten. Dies ist unerlässlich, wenn man bedenkt, dass die in C geschriebene Extension später als vollwertiges Modul auftreten soll und dass auf keinen Fall Probleme an der Schnittstelle, wie sie beispielsweise mit ctypes auftreten, vorkommen dürfen. Auch das Werfen von Python-Exceptions aus einer C-Extension heraus wird mithilfe der Python API möglich.

Sie werden in diesem Kapitel nur eine knappe Referenz zur Python API finden, da wir hier primär einen praxisorientierten Einstieg in das Schreiben von Extensions bieten möchten. Eine umfassende Referenz finden Sie in der Python-Dokumentation unter dem Stichwort »Python/C API«. Die Dokumentation enthält außerdem ausführliche Informationen zum Schreiben von Erweiterungen bzw. zum Einbetten des Python-Interpreters in ein C-Programm unter dem Stichwort Extending and Embedding Python.


Galileo Computing - Zum Seitenanfang

26.2.1 Ein einfaches Beispiel  Zur nächsten ÜberschriftZur vorigen Überschrift

In diesem Abschnitt soll eine einfache Beispiel-Extension in C geschrieben werden. Wir nehmen uns vor, eine Extension namens chiffre zu schreiben, die verschiedene Funktionen zur Verschlüsselung eines Strings bereitstellt. Da es sich um ein Beispiel handelt, werden wir uns auf eine einzelne Funktion namens caesar beschränken, die eine Verschiebechiffre, auch »Cäsar-Verschlüsselung« genannt, durchführen soll. Die Funktion soll später folgendermaßen verwendet werden können:

>>> chiffre.caesar("HALLOWELT", 13) 
'UNYYBJRYG'

Dabei entspricht der zweite Parameter der Anzahl Stellen, um die jeder Buchstabe des ersten Parameters verschoben wird.

Im Folgenden werden wir eine Extension schreiben, die das Modul chiffre inklusive der Funktion caesar für ein Python-Programm bereitstellt. Die Quelldatei der Erweiterung lautet chiffre.c. Der Quelltext der Erweiterung soll nun Schritt für Schritt besprochen werden.

#include <Python.h>
static PyObject *chiffre_caesar(PyObject *self, PyObject *args);

Zunächst wird der Header der Python API eingebunden. Sie finden die Header-Datei Python.h im Unterordner include Ihrer Python-Installation. Außerdem schreiben wir zu Beginn der Quelldatei den Prototyp der Funktion chiffre_caesar, die später der Funktion caesar des Moduls chiffre entsprechen soll.

Beachten Sie, dass in der Header-Datei Python.h einige Präprozessor-Anweisungen enthalten sind, die sich auf andere Header-Dateien auswirken. Aus diesem Grund sollte Python.h immer vor den Standard-Headern eingebunden werden.

Dann wird die sogenannte Method Table erstellt, die alle Funktionen der Extension auflistet:

static PyMethodDef ChiffreMethods[] = 
    { 
    {"caesar", chiffre_caesar, METH_VARARGS, 
     "Perform Caesar cipher encryption."}, 
    {NULL, NULL, 0, NULL} 
    };

Jeder Eintrag der Method Table enthält zunächst den Namen, den die Funktion oder Methode in Python tragen soll, dann den Namen der assoziierten C-Funktion, dann die Kennzeichnung der Art der Parameterübergabe und schlussendlich eine Beschreibung der Funktion als String. Das Makro METH_VARARGS besagt, dass alle Parameter, die der Funktion caesar in Python übergeben werden, in der C–Funktion chiffre_caesar in Form eines Tupels ankommen. Dies ist die bevorzugte Art der Parameterübergabe.

Ein mit Nullen gefüllter Eintrag beendet die Method Table.

Es folgt die Initialisierungsfunktion der Erweiterung namens initchiffre:

PyMODINIT_FUNC initchiffre(void) 
    { 
    Py_InitModule("chiffre", ChiffreMethods); 
    }

Sie wird vom Interpreter aufgerufen, wenn das Modul chiffre zum ersten Mal eingebunden wird, und hat die Aufgabe, das Modul einzurichten und dem Interpreter die Method Table (ChiffreMethods) zu übergeben. Dazu wird die Funktion Py_InitModule aufgerufen.

Beachten Sie, dass die Funktion initchiffre genannt werden muss, wobei chiffre natürlich bei einem anderen Modulnamen entsprechend angepasst werden muss.

Jetzt folgt die Funktion chiffre_caesar. Das ist die C-Funktion, die bei einem Aufruf der Python-Funktion chiffre.caesar aufgerufen wird.

static PyObject *chiffre_caesar(PyObject *self, PyObject *args) 
    { 
    char *text, *c, *e; 
    PyObject *ergebnis; 
    int cipher;
if(!PyArg_ParseTuple(args, "si", &text, &cipher)) return NULL;
ergebnis = PyString_FromStringAndSize(NULL, strlen(text)); e = PyString_AsString(ergebnis);
for(c = text; *c; c++, e++) *e = ((*c - 'A' + cipher) % 26) + 'A';
return ergebnis; }

Dabei werden als Parameter immer zwei Pointer auf eine PyObject-Struktur übergeben. Eine solche PyObject-Struktur entspricht ganz allgemein einer Referenz auf ein Python-Objekt. In C werden also alle Instanzen aller Datentypen auf PyObject-Strukturen abgebildet. Durch Funktionen der Python-API lassen sich dann datentyp-spezifische Eigenschaften der Instanzen auslesen. Doch kommen wir nun zur Bedeutung der übergebenen Parameter im Einzelnen.

Der erste Parameter, self, würde nur dann benötigt, wenn die Funktion chiffre_caesar eine Python-Methode implementieren würde, und ist in diesem Beispiel immer NULL. Der zweite Parameter, args, ist ein Tupel und enthält alle der Python-Funktion übergebenen Parameter. Auf die Parameter kann über die API-Funktion PyArg_ParseTuple zugegriffen werden.

Die Funktion PyArg_ParseTuple bekommt zunächst den Parameter args selbst übergeben und danach einen String, der die Datentypen der enthaltenen Parameter kennzeichnet. s steht dabei für einen String und i für eine ganze Zahl. Im Folgenden zeigt text auf den übergebenen String, während cipher den zweiten übergebenen Parameter, die ganze Zahl, enthält. Beachten Sie, dass text auf den Inhalt des übergebenen Python-Strings zeigt und aus diesem Grund nicht verändert werden darf.

Nachdem der zu verschlüsselnde Text in text und die zu verwendende Anzahl Stellen in cipher stehen, kann die tatsächliche Verschlüsselung durchgeführt werden. Dazu wird zunächst ein neuer String mit der Länge des Strings text erzeugt. Da dieser String gerade erst erzeugt worden ist und somit keine anderen Referenzen auf ihn verweisen können, dürfen wir im Folgenden in den internen Buffer des Strings schreiben. Zum Verschlüsseln des Strings wird in einer Schleife über alle Buchstaben des übergebenen Strings iteriert und jeder Buchstabe um die angegebene Anzahl Stellen verschoben. Der auf diese Weise veränderte Buchstabe wird dann in den neu erstellten String geschrieben.

Beachten Sie, dass dieser Algorithmus weder für Kleinbuchstaben noch für Sonderzeichen, sondern ausschließlich für Großbuchstaben funktioniert. Diese Einschränkung erhöht nicht nur die Übersicht, sondern erlaubt es uns später, einen – zugegebenermaßen künstlichen – Fehlerfall zu erzeugen.


Galileo Computing - Zum Seitenanfang

26.2.2 Exceptions  Zur nächsten ÜberschriftZur vorigen Überschrift

Wir haben bereits gesagt, dass der in der Funktion chiffre_caesar verwendete Algorithmus nur mit Strings arbeiten kann, die allein aus ASCII-Großbuchstaben bestehen. Es wäre natürlich ein Leichtes, die Funktion chiffre_caesar dahingehend anzupassen, dass auch Kleinbuchstaben verschlüsselt und Sonderzeichen übersprungen werden. Doch zu Demonstrationszwecken soll in diesem Beispiel stattdessen eine ValueError-Exception geworfen werden, wenn der übergebene String nicht ausschließlich aus ASCII-Großbuchstaben besteht.

Eine eingebaute Exception kann mithilfe der Funktion PyErr_SetString geworfen werden, wobei der Funktion der Exceptiontyp, in diesem Fall PyExc_ValueError, und die Fehlerbeschreibung übergeben wird. Die Funktion chiffre_caesar sieht inklusive Fehlerbehandlung so aus:

static PyObject *chiffre_caesar(PyObject *self, PyObject *args) 
    { 
    char *text, *c, *e; 
    PyObject *ergebnis; 
    int cipher;
if(!PyArg_ParseTuple(args, "si", &text, &cipher)) return NULL;
ergebnis = PyString_FromStringAndSize(NULL, strlen(text)); e = PyString_AsString(ergebnis);
for(c = text; *c; c++, e++) { if((*c < 'A') || (*c > 'Z')) { PyErr_SetString(PyExc_ValueError, "Character out of range"); Py_DECREF(ergebnis); return NULL; }
*e = ((*c - 'A' + cipher) % 26) + 'A'; } return ergebnis; }

Direkt nach dem Setzen der Exception wird das Makro Py_DECREF verwendet. Dieses Makro ist dazu da, den Reference Count der Instanz ergebnis zu verringern. Näheres dazu erfahren Sie in Abschnitt 26.2.4.

Nachdem die Extension kompiliert, gelinkt und installiert wurde, (Näheres zu diesen Vorgängen erfahren Sie im nächsten Abschnitt), können wir sie tatsächlich im interaktiven Modus ausprobieren:

>>> import chiffre 
>>> chiffre.caesar("HALLOWELT", 13) 
'UNYYBJRYG' 
>>> chiffre.caesar("UNYYBJRYG", 13) 
'HALLOWELT' 
>>> chiffre.caesar("Hallo Welt", 13) 
Traceback (most recent call last): 
  File "<stdin>", line 1, in <module> 
ValueError: Character out of range

Beachten Sie, dass wir die Cäsar-Verschlüsselung bislang immer mit dem Verschiebungsparameter 13 durchgeführt haben. Das entspricht dem ROT13-Algorithmus, der eine relativ große Bekanntheit genießt (was aber nicht heißt, dass er besonders sicher wäre). Der Vorteil von ROT13 ist, dass der verschlüsselte String durch nochmaliges Verschlüsseln mit ROT13 entschlüsselt wird, wie auch im obigen Beispiel zu sehen ist. Selbstverständlich sind aber auch andere Verschiebungsparameter möglich.


Galileo Computing - Zum Seitenanfang

26.2.3 Erzeugen der Extension  Zur nächsten ÜberschriftZur vorigen Überschrift

Da die soeben geschriebene Quelldatei chiffre.c in einer ganz bestimmten Art kompiliert und gelinkt werden muss, kommt es uns nur zupass, dass das Paket distutils auch das Kompilieren und Linken von Erweiterungen automatisiert.

Beachten Sie, dass zum Kompilieren der Extension ein C-Compiler auf Ihrem System installiert sein muss. Unter Linux wird vom Paket distutils der GCC- und unter Windows der MSVC-Compiler von Visual Studio 2003 (sofern es installiert ist) verwendet. Visual Studio kann inzwischen in einer eingeschränkten Version kostenlos von Microsoft bezogen werden. Sollten Sie ein Windows-System einsetzen und Visual Studio 2003 nicht installiert haben, bietet Ihnen das Paket distutils an, stattdessen eine MinGW-Installation zu verwenden.

Das Installationsscript setup.py sieht in Bezug auf unsere einfache Beispielextension folgendermaßen aus:

from distutils.core import setup, Extension
modul = Extension("chiffre", sources=["chiffre.c"]) setup( name = "PyChiffre", version = "1.0", description = "Module for encryption techniques.", ext_modules = [modul] )

Zunächst wird eine Instanz der Klasse Extension erzeugt und ihrem Konstruktor der Name der Extension und eine Liste der zugrunde liegenden Quelldateien übergeben. Beim Aufruf der Funktion setup wird, abgesehen von den üblichen Parametern, der Schlüsselwortparameter ext_modules übergeben. Dort muss eine Liste von Extension-Instanzen übergeben werden, die mit dem Installationsscript kompiliert, gelinkt und in die Distribution aufgenommen werden sollen.

Jetzt kann das Installationsscript wie gewohnt ausgeführt werden und kompiliert bzw. installiert die Erweiterung automatisch.

Neben dem Schlüsselwortparameter sources können bei der Instanziierung der Klasse Extension noch weitere Parameter übergeben werden, die in der folgenden Tabelle kurz erläutert werden.


Tabelle 26.2  Schlüsselwortparameter des Extension-Konstruktors
Parameter Bedeutung
include_dirs

Eine Liste von Verzeichnissen, die für das Kompilieren der Erweiterung benötigte Header-Dateien enthalten

define_macros

Eine Liste von Tupeln, über die beim Kompilieren der Erweiterung bestimmte Makros mit bestimmten Werten definiert werden können. Das Tupel muss folgende Struktur haben: ("MAKRONAME", "Wert").

undef_macros

Eine Liste von Makronamen, die beim Kompilieren nicht definiert sein sollen

libraries

Eine Liste von Bibliotheksnamen, gegen die die Erweiterung gelinkt werden soll

librariy_dirs

Eine Liste von Verzeichnissen, in denen nach den bei libraries angegebenen Bibliotheken gesucht wird


Nachdem die Extension mittels distutils kompiliert und installiert wurde, kann sie in einer Python Shell verwendet werden:

>>> import chiffre 
>>> chiffre.caesar("HALLOWELT", 13) 
'UNYYBJRYG' 
>>> chiffre.caesar("UNYYBJRYG", 13) 
'HALLOWELT'

Galileo Computing - Zum Seitenanfang

26.2.4 Reference Counting  topZur vorigen Überschrift

Wie Sie wissen, basiert die Speicherverwaltung Pythons auf einem sogenannten Reference-Counting-Algorithmus. Das bedeutet, dass Instanzen zur Entsorgung freigegeben werden, sobald keine Referenzen mehr auf sie bestehen. Das hat den Vorteil, dass sich der Programmierer nicht um das Freigeben von allokiertem Speicher zu kümmern braucht.

Vermutlich wissen Sie ebenfalls, dass es so etwas wie Reference Counting in C nicht gibt, sondern dass dort der Programmierer für die Speicherverwaltung selbst verantwortlich ist. Wie verträgt es sich damit also, wenn man Python-Instanzen in einem C-Programm verwendet?

Grundsätzlich sollten Sie sich von dem C-Idiom verabschieden, im Besitz einer bestimmten Instanz bzw. in diesem Fall einer PyObject-Struktur zu sein. Vielmehr können Sie allenfalls im Besitz einer Referenz auf eine Instanz bzw. eines Pointers auf eine PyObject-Struktur sein. Damit implementiert die Python API im Grunde das Speichermodell von Python in C. Im Gegensatz zum Speichermodell von Python erhöht bzw. verringert sich der Referenzzähler einer Instanz jedoch nicht automatisch, sondern muss in einer C-Extension explizit mitgeführt werden. Dazu können die Makros Py_INCREF und Py_DECREF der Python API folgendermaßen verwendet werden:

PyObject *string = PyString_FromString("Hallo Welt"); 
Py_INCREF(string); 
Py_DECREF(string); 
Py_DECREF(string);

Zunächst wird mithilfe der Funktion PyString_FromString eine Instanz des Python-Datentyps str erzeugt. In diesem Moment besitzen Sie eine Referenz auf diese Instanz. Der Reference Count ist damit gleich 1. Im Folgenden wird der Reference Count durch die Makros Py_INCREF und Py_DECREF einmal erhöht und zweimal verringert. Am Ende des Beispiels erreicht der Reference Count 0, und der erzeugte String wird der Garbage Collection zum Fraß vorgeworfen.

Das hier besprochene Beispiel ist zugegebenermaßen nicht gerade sinnvoll. Im nächsten Abschnitt wird jedoch ein Beispielprogramm erarbeitet, in dem die Anwendung des Reference Countings in der Praxis zu sehen ist.

Jetzt bliebe nur noch die Frage zu klären, wann Sie den Referenzzähler erhöhen bzw. verringern müssen. Immer dann, wenn Sie in Ihrem Programm eine Instanz eines Python-Datentyps erzeugen und eine oder mehrere Referenzen auf diese Instanz halten, müssen Sie diese Referenzen freigeben, wenn sie nicht mehr benötigt werden. Sollten Sie die Referenzen nicht freigeben, verweilt die Instanz im Speicher, obwohl sie eigentlich nicht mehr benötigt wird. Es handelt sich dann um ein sogenanntes Memory Leak, und Memory Leaks sind kein besonders erstrebenswerter Umstand in einem Programm.

Die zweite Möglichkeit sind sogenannte geliehene Referenzen (engl. borrowed references). Solche Referenzen sind beispielsweise über Funktionsparameter an Ihr Programm weitergereicht worden, gehören Ihnen aber nicht. Das bedeutet auch, dass Sie solche Referenzen nicht freizugeben brauchen. Ein Beispiel für geliehene Referenzen sind Funktionsparameter, die Sie grundsätzlich nicht freigeben brauchen. Wenn Sie eine geliehene Referenz zu einer eigenen Referenz aufwerten möchten, müssen Sie den Referenzzähler der dahinter liegenden Instanz mittels Py_INCREF erhöhen. Das Freigeben von geliehenen Referenzen führt dazu, dass danach möglicherweise auf Speicherbereiche zugegriffen wird, die bereits freigegeben worden sind. Das kann in einigen Fällen gut gehen, führt aber häufig zu einem Speicherzugriffsfehler. Ähnlich wie Memory Leaks sollten Sie Speicherzugriffsfehler nach Möglichkeit vermeiden.

Als letzte Möglichkeit können Sie eine vollwertige Referenz in Form eines Rückgabewertes an eine andere Funktion abgeben. Sie brauchen sich also nicht um die Freigabe Ihrer zurückgegebenen Instanzen kümmern.



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.






 <<   zurück
  
  Zum Katalog
Zum Katalog: Python






Python
bestellen
 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: Linux






 Linux


Zum Katalog: Ubuntu GNU/Linux






 Ubuntu GNU/Linux


Zum Katalog: Praxisbuch Web 2.0






 Praxisbuch Web 2.0


Zum Katalog: UML 2.0






 UML 2.0


Zum Katalog: Praxisbuch Objektorientierung






 Praxisbuch Objektorientierung


Zum Katalog: Einstieg in SQL






 Einstieg in SQL


Zum Katalog: IT-Handbuch für Fachinformatiker






 IT-Handbuch für Fachinformatiker


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo





Copyright © Galileo Press 2008
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de