Rheinwerk Computing < openbook >

 
Inhaltsverzeichnis
1 Einleitung
2 Die Programmiersprache Python
Teil I Einstieg in Python
3 Erste Schritte im interaktiven Modus
4 Der Weg zum ersten Programm
5 Kontrollstrukturen
6 Dateien
7 Das Laufzeitmodell
8 Funktionen, Methoden und Attribute
9 Informationsquellen zu Python
Teil II Datentypen
10 Das Nichts – NoneType
11 Operatoren
12 Numerische Datentypen
13 Sequenzielle Datentypen
14 Zuordnungen
15 Mengen
16 Collections
17 Datum und Zeit
18 Aufzählungstypen – Enum
Teil III Fortgeschrittene Programmiertechniken
19 Funktionen
20 Modularisierung
21 Objektorientierung
22 Ausnahmebehandlung
23 Iteratoren
24 Kontextobjekte
25 Manipulation von Funktionen und Methoden
Teil IV Die Standardbibliothek
26 Mathematik
27 Kryptografie
28 Reguläre Ausdrücke
29 Schnittstelle zu Betriebssystem und Laufzeitumgebung
30 Kommandozeilenparameter
31 Dateisystem
32 Parallele Programmierung
33 Datenspeicherung
34 Netzwerkkommunikation
35 Debugging und Qualitätssicherung
36 Dokumentation
Teil V Weiterführende Themen
37 Anbindung an andere Programmiersprachen
38 Distribution von Python-Projekten
39 Grafische Benutzeroberflächen
40 Python als serverseitige Programmiersprache im WWW – ein Einstieg in Django
41 Wissenschaftliches Rechnen
42 Insiderwissen
43 Von Python 2 nach Python 3
A Anhang
Stichwortverzeichnis

Download:
- Beispielprogramme, ca. 464 KB

Jetzt Buch bestellen
Ihre Meinung?

Spacer
<< zurück
Python 3 von Johannes Ernesti, Peter Kaiser
Das umfassende Handbuch
Buch: Python 3

Python 3
Pfeil 37 Anbindung an andere Programmiersprachen
Pfeil 37.1 Dynamisch ladbare Bibliotheken – ctypes
Pfeil 37.1.1 Ein einfaches Beispiel
Pfeil 37.1.2 Die eigene Bibliothek
Pfeil 37.1.3 Datentypen
Pfeil 37.1.4 Schnittstellenbeschreibung
Pfeil 37.1.5 Pointer
Pfeil 37.1.6 Strings
Pfeil 37.2 Schreiben von Extensions
Pfeil 37.2.1 Ein einfaches Beispiel
Pfeil 37.2.2 Exceptions
Pfeil 37.2.3 Erzeugen der Extension
Pfeil 37.2.4 Reference Counting
Pfeil 37.3 Python als eingebettete Skriptsprache
Pfeil 37.3.1 Ein einfaches Beispiel
Pfeil 37.3.2 Ein komplexeres Beispiel
Pfeil 37.4 Alternative Interpreter
Pfeil 37.4.1 Interoperabilität mit der Java Runtime Environment – Jython
Pfeil 37.4.2 Interoperabilität mit .NET – IronPython
 
Zum Seitenanfang

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

Im letzten Abschnitt haben wir uns damit beschäftigt, dynamische Bibliotheken zu laden und aus einem Python-Programm heraus anzusprechen. Jetzt behandeln wir das Schreiben von Python-Modulen in C. Solche Module werden Extensions (dt. »Erweiterungen«) genannt.

Extensions werden mithilfe der sogenannten Python API geschrieben. Die Python API enthält beispielsweise Funktionen zur Verarbeitung von Python-Datentypen in C. Auch das Werfen von Python-Exceptions aus einer C-Extension heraus wird mithilfe der Python API möglich. Beides ist unerlässlich, wenn man bedenkt, dass die in C geschriebene Extension später als vollwertiges Modul auftreten soll. Der Benutzer soll keinen Unterschied in der Schnittstelle zwischen einem Python-Modul und einer C-Extension bemerken können.

[»]  Hinweis

Die Python API ist sehr umfangreich und kann an dieser Stelle nicht erschöpfend behandelt werden. Stattdessen ermöglichen Ihnen die folgenden Abschnitte einen praxisorientierten Einblick in das Schreiben von Extensions.

Eine umfassende Referenz zur Python API finden Sie in der Python-Dokumentation unter den Stichworten »Python/C API« und »Extending and Embedding the Python Interpreter«.

 
Zum Seitenanfang

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

In diesem Abschnitt nehmen wir uns vor, eine einfache Extension namens chiffre zu erstellen, 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ührt. 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 wird nun Schritt für Schritt besprochen.

#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.[ 162 ](Das Schlüsselwort static bedeutet in diesem Fall, dass der Name chiffre_caesar nur innerhalb der Quelldatei chiffre.c mit der angelegten Funktion verknüpft ist. Dieses Verhalten wird »internal linkage« genannt. )

[»]  Hinweis

Der Header Python.h enthält einige Präprozessor-Anweisungen, 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 schließlich 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.

Nach der Method Table muss noch eine zweite Struktur gefüllt werden, die Informationen über unser Modul chiffre enthält:

static PyModuleDef ChiffreModule = 
{
PyModuleDef_HEAD_INIT,
"chiffre",
"Performs insane encryption operations",
-1,
ChiffreMethods
};

Die in diesem Beispiel wichtigen Angaben sind der Modulname und der Docstring des Moduls an zweiter bzw. dritter sowie die zuvor erstellte Method Table an letzter Stelle.

Es folgt die Initialisierungsfunktion der Erweiterung namens PyInit_chiffre:

PyMODINIT_FUNC PyInit_chiffre(void)
{
return PyModule_Create(&ChiffreModule);
}

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 Moduldefinition (ChiffreModule) zu übergeben. Dazu wird die Funktion PyModule_Create aufgerufen.

Die Funktion muss PyInit_chiffre genannt werden, wobei Sie chiffre bei einem anderen Modulnamen entsprechend anpassen müssen.

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, *encrypted, *c, *e;
PyObject *result = NULL;
int cipher, length;
if(!PyArg_ParseTuple(args, "si", &text, &cipher))
return NULL;
length = strlen(text);
encrypted = (char *)malloc(length+1);
encrypted[length] = '\0';
for(c = text, e = encrypted; *c; c++, e++)
*e = ((*c - 'A' + cipher) % 26) + 'A';
result = Py_BuildValue("s", encrypted);
free(encrypted);
return result;
}

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 datentypspezifische Eigenschaften der Instanzen auslesen. Doch kommen wir nun zur Bedeutung der übergebenen Parameter im Einzelnen.

Der erste Parameter, self, wird nur dann benötigt, wenn die Funktion chiffre_caesar eine Python-Methode implementiert, 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. Diese Funktion bekommt zunächst den Parameter args selbst übergeben und danach einen String, der die Datentypen der enthaltenen Parameter kennzeichnet. 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 C-String mit der Länge des Strings text erzeugt, der später den verschlüsselten Text enthalten soll. Zum Verschlüsseln wird dann 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 encrypted 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 auch, einen – zugegebenermaßen künstlichen – Fehlerfall zu erzeugen. Zuvor werden aber noch die beiden Funktionen Py_BuildValue und PyArg_ParseTuple im Detail besprochen.

PyObject *Py_BuildValue(const char *format, …)

Diese Funktion erzeugt eine Instanz eines Python-Datentyps mit einem bestimmten Wert. Der String format spezifiziert dabei den Datentyp. Tabelle 37.2 listet die wichtigsten Werte für format mit ihrer jeweiligen Bedeutung auf.

Format-String Beschreibung
"s" Erzeugt eine Instanz des Python-Datentyps str aus einem C-String (char *).
"y" Erzeugt eine Instanz des Python-Datentyps bytes aus einem C‐String (char *).
"u" Erzeugt eine Instanz des Python-Datentyps str aus einem C-Buffer mit Unicode-Daten (Py_UNICODE *).
"i" Erzeugt eine Instanz des Python-Datentyps int aus einem C-Integer (int).
"c" Erzeugt eine Instanz des Python-Datentyps str aus einem C‐Zeichen (char). Der resultierende String hat die Länge 1.
"d" Erzeugt eine Instanz des Python-Datentyps float aus einer C-Gleitkommazahl (double).
"f" Erzeugt eine Instanz des Python-Datentyps float aus einer C-Gleitkommazahl (float).
"(…)" Erzeugt eine Instanz des Python-Datentyps tuple. Anstelle der Auslassungszeichen müssen die Datentypen der Elemente des Tupels angegeben werden. "(iii)" würde beispielsweise ein Tupel mit drei ganzzahligen Elementen erzeugen.
"[…]" Erzeugt eine Instanz des Python-Datentyps list. Der Format-String "[iii]" erzeugt zum Beispiel eine Liste mit drei ganzzahligen Elementen.
"{…}" Erzeugt eine Instanz des Python-Datentyps dict. Der Format-String "{i:s,i:s}" erzeugt ein Dictionary mit zwei Schlüssel-Wert-Paaren, die jeweils aus einer ganzen Zahl als Schlüssel und einem String als Wert bestehen.

Tabelle 37.2    Mögliche Angaben im Format-String

Ein C-String wird stets kopiert, wenn er an die Funktion Py_BuildValue übergeben wird, um einen Python-String zu erzeugen. Das bedeutet insbesondere, dass Sie jeden dynamisch allokierten String wieder freigeben müssen, selbst wenn er an die Funktion Py_BuildValue übergeben wurde.

int PyArg_ParseTuple(PyObject *args, const char *format, …)

Die Funktion PyArg_ParseTuple liest die einer Funktion übergebenen Positionsargumente aus und speichert sie in lokalen Variablen. Als erster Parameter muss ein Tupel übergeben werden, das die Parameter enthält. Ein solches Tupel bekommt jede Python-Funktion in C übergeben.

Der zweite Parameter format ist ein String, ähnlich dem Format-String von Py_BuildValue, und legt fest, wie viele Parameter ausgelesen werden und welche Datentypen diese haben sollen. Zum Schluss akzeptiert die Funktion PyArg_ParseTuple beliebig viele Pointer auf Variablen, die mit den ausgelesenen Werten gefüllt werden sollen.

Im Erfolgsfall gibt die Funktion True zurück. Bei einem Fehler wirft sie eine entsprechende Exception und gibt False zurück.

 
Zum Seitenanfang

37.2.2    Exceptions Zur vorigen ÜberschriftZur nächsten Überschrift

Wir haben bereits gesagt, dass der in der Funktion chiffre_caesar verwendete Algorithmus nur mit Strings arbeiten kann, die ausschließlich aus 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 Großbuchstaben besteht.

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

static PyObject *chiffre_caesar(PyObject *self, PyObject *args)
{
char *text, *encrypted, *c, *e;
PyObject *result = NULL;
int cipher, length;
if(!PyArg_ParseTuple(args, "si", &text, &cipher))
return NULL;
length = strlen(text);
encrypted = (char *)malloc(length+1);
encrypted[length] = '\0';
for(c = text, e = encrypted; *c; c++, e++)
{
if((*c < 'A') || (*c > 'Z'))
{
PyErr_SetString(PyExc_ValueError, "Character out of range");
return NULL;
}
*e = ((*c - 'A' + cipher) % 26) + 'A';
}
result = Py_BuildValue("s", encrypted);
free(encrypted);
return result;
}

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
[»]  Hinweis

Bislang haben wir die Cäsar-Verschlüsselung immer mit dem Verschiebungsparameter 13 durchgeführt. Das entspricht dem ROT13-Algorithmus, der eine große Bekanntheit genießt.[ 163 ](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 oben dargestellten Beispiel zu sehen ist. Selbstverständlich sind aber auch andere Verschiebungsparameter möglich.

 
Zum Seitenanfang

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

Um eine Extension zu erzeugen, verwenden wir das Paket setuptools, das die dazu notwendigen Schritte automatisiert. Zum Kompilieren der Extension muss ein C-Compiler auf Ihrem System installiert sein. Unter Linux wird von setuptools der GCC- und unter Windows der MSVC-Compiler von Visual Studio[ 164 ](Das Visual Studio erhalten Sie in einer eingeschränkten Version kostenlos von Microsoft. ) verwendet. Sollten Sie ein Windows-System einsetzen und Visual Studio nicht installiert haben, bietet Ihnen das Paket setuptools an, stattdessen eine MinGW-Installation zu verwenden.

Das Installationsskript setup.py sieht in Bezug auf unsere einfache Beispiel-Extension folgendermaßen aus:

from setuptools 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 Installationsskript kompiliert, gelinkt und in die Distribution aufgenommen werden sollen.

Jetzt kann das Installationsskript 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:

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 Makros 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 Bibliotheken, gegen die die Erweiterung gelinkt werden soll
library_dirs eine Liste von Verzeichnissen, in denen nach den bei libraries angegebenen Bibliotheken gesucht wird

Tabelle 37.3    Schlüsselwortparameter des Extension-Konstruktors

Nachdem die Extension mit setuptools kompiliert und installiert wurde, kann sie in einer Python-Shell verwendet werden:

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

37.2.4    Reference Counting Zur vorigen ÜberschriftZur nächsten Überschrift

Wie Sie wissen, basiert die Speicherverwaltung von Python auf einem 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 = PyBytes_FromString("Hallo Welt");
Py_INCREF(string);
Py_DECREF(string);
Py_DECREF(string);

Zunächst wird mithilfe der Funktion PyBytes_FromString eine Instanz des Python-Datentyps bytes 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.

[»]  Hinweis

Der den Makros Py_INCREF und Py_DECREF übergebene Pointer darf nicht den Wert NULL haben. Sollte dies nicht garantiert sein, können Sie die Makros Py_XINCREF bzw. Py_XDECREF verwenden, die den übergebenen Pointer vorher überprüfen.

Jetzt bleibt 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 Memory Leak[ 165 ](Ein Memory Leak ist ein Fehler in einer Anwendung, durch den ein allokierter Speicherbereich von dieser Anwendung nicht mehr verwendet oder freigegeben werden kann, beispielsweise weil alle Pointer auf diesen Bereich überschrieben wurden. Solche Speicherbereiche können erst nach Beendigung der Anwendung vom Betriebssystem freigegeben werden. ), und Memory Leaks sind kein erstrebenswerter Umstand in einem Programm.

Die zweite Möglichkeit sind geliehene Referenzen (engl. borrowed references). Solche Referenzen gehören Ihnen nicht. Das bedeutet, dass sie nicht von Ihnen freigegeben werden müssen. Ein Beispiel für geliehene Referenzen sind Funktionsparameter. Wenn Sie eine geliehene Referenz zu einer eigenen Referenz aufwerten möchten, müssen Sie den Referenzzähler der dahinterliegenden Instanz mittels Py_INCREF erhöhen. Das Freigeben geliehener Referenzen führt dazu, dass daraufhin möglicherweise auf bereits freigegebene Speicherbereiche zugegriffen wird. 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 geben Sie eine vollwertige Referenz in Form eines Rückgabewertes an eine andere Funktion ab. Sie brauchen sich also nicht um die Freigabe Ihrer zurückgegebenen Instanzen zu kümmern.

 


Ihre Meinung

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.

<< zurück
 Zum Rheinwerk-Shop
Zum Rheinwerk-Shop: Python 3 Python 3
Jetzt Buch bestellen

 Buchempfehlungen
Zum Rheinwerk-Shop: Einstieg in Python
Einstieg in Python


Zum Rheinwerk-Shop: Python. Der Grundkurs
Python. Der Grundkurs


Zum Rheinwerk-Shop: Algorithmen mit Python
Algorithmen mit Python


Zum Rheinwerk-Shop: Objektorientierte Programmierung
Objektorientierte Programmierung


Zum Rheinwerk-Shop: Raspberry Pi. Das umfassende Handbuch
Raspberry Pi. Das umfassende Handbuch


Zum Rheinwerk-Shop: Roboter-Autos mit dem Raspberry Pi
Roboter-Autos mit dem Raspberry Pi


Zum Rheinwerk-Shop: Neuronale Netze programmieren mit Python
Neuronale Netze programmieren mit Python


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
InfoInfo

 
 


Copyright © Rheinwerk Verlag GmbH 2020
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.

 
[Rheinwerk Computing]

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern