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    Anbindung an andere Programmiersprachen Zur vorigen ÜberschriftZur nächsten Überschrift

Dieses Kapitel beschäftigt sich mit der Interoperabilität zwischen Python und anderen Programmiersprachen. Dazu werden drei wesentliche Szenarien besprochen, deren Fokus auf der Interoperabilität mit den Programmiersprachen C und C++ sowie den Laufzeitumgebungen JRE und .NET liegt:

  1. C-Code in einem Python-Programm ausführen
    In einem größeren Projekt kann Python als komfortable und gut zu wartende Sprache beispielsweise für die Programmlogik eingesetzt werden, während man einige wenige zeitkritische Algorithmen des Projekts aus Effizienzgründen in einer nicht interpretierten Sprache wie C oder C++ schreibt. Zu diesem Ansatz besprechen wir, wie Sie mit dem Modul ctypes der Standardbibliothek auf dynamische Bibliotheken, beispielsweise Windows-DLLs, zugreifen können.
  2. Python-Code in einem C-Programm ausführen
    Häufig möchte man auch den umgekehrten Weg beschreiten und in einem größeren C/C++-Projekt Python als eingebettete Skriptsprache für dynamische Elemente des Programms verwenden. In einem Computerspiel könnte beispielsweise C/C++ für die hauptsächlich laufzeitkritische Hauptanwendung und die gesamte Algorithmik verwendet werden, während Python für die dynamischen Elemente des Spiels, beispielsweise Ereignisse in bestimmten Levels oder das Verhalten verschiedener Spielfiguren, genutzt wird. Dieser Ansatz wird in Abschnitt 37.3, »Python als eingebettete Skriptsprache«, verfolgt.
  3. Python-Programme für andere Laufzeitumgebungen schreiben
    Um die Programmiersprache Python in anderen, eigentlich fremden Laufzeitumgebungen wie der Java Runtime Environment (JRE) oder Microsofts .NET Framework zu verwenden, stehen die alternativen Interpreter Jython und IronPython zur Verfügung, die anstelle des Standard-Interpreters CPython zur Ausführung von Python-Code verwendet werden können. Diese alternativen Interpreter sind vollständig in Java bzw. C# geschrieben und passen sich daher nahtlos in die jeweilige Laufzeitumgebung ein. Auf diese Weise ist es beispielsweise möglich, Python als Skriptsprache in Java-Programmen zu verwenden oder aus einem Python-Programm heraus auf die umfangreichen Java-Bibliotheken zuzugreifen. Näheres zu Jython und IronPython erfahren Sie in Abschnitt 37.4, »Alternative Interpreter«.
 
Zum Seitenanfang

37.1    Dynamisch ladbare Bibliotheken – ctypes Zur vorigen ÜberschriftZur nächsten Überschrift

Mit dem Modul ctypes ist es möglich, Funktionen einer dynamisch ladbaren Bibliothek, im Folgenden dynamische Bibliothek genannt, aufzurufen. Zu solchen dynamischen Bibliotheken zählen beispielsweise DLL-Dateien (Dynamic Link Library) unter Windows oder SO-Dateien (Shared Object) unter Linux bzw. Unix.

Das Aufrufen von Funktionen einer dynamischen Bibliothek ist besonders dann sinnvoll, wenn bestimmte laufzeitkritische Teile eines Python-Programms in einer hardwarenäheren und damit effizienteren Programmiersprache geschrieben werden sollen oder wenn Sie schlicht eine in C oder C++ geschriebene Bibliothek in Python nutzen möchten.

Beachten Sie, dass das Erstellen einer dynamischen Bibliothek keine Eigenschaft der Programmiersprache C ist. Im Gegenteil: Eine dynamische Bibliothek kann als eine sprachunabhängige Schnittstelle zwischen verschiedenen Programmen betrachtet werden. Es ist beispielsweise möglich, ein Python-Programm zu schreiben, das auf eine in C geschriebene dynamische Bibliothek zugreift, die ihrerseits auf eine dynamische Bibliothek zugreift, die in Pascal geschrieben wurde.

 
Zum Seitenanfang

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

Zum Einstieg in das Modul ctypes dient ein einfaches Beispiel. Im Beispielprogramm soll die dynamische Bibliothek der C Runtime Library eingebunden und die darin enthaltene Funktion printf dazu genutzt werden, den Text »Hallo Welt« auszugeben. Die C Runtime Library ist unter Windows unter dem Namen msvcrt.dll und unter Unix-ähnlichen Systemen unter dem Namen libc.so.6 zu finden. Dazu betrachten wir zunächst den Quellcode des Beispielprogramms:

import ctypes
bibliothek = ctypes.CDLL("MSVCRT")
bibliothek.printf(b"Hallo Welt\n")

Zunächst wird das Modul ctypes eingebunden und dann eine Instanz der Klasse CDLL[ 159 ](Die Klasse CDLL ist nur für Bibliotheken zuständig, die der cdecl-Aufrufkonvention genügen. Gerade Bibliotheken der Windows-API verwenden jedoch die stdcall-Konvention. Zum Laden solcher Bibliotheken existiert unter Windows die Klasse WinDLL. ) erzeugt. Eine Instanz dieser Klasse repräsentiert eine geladene dynamische Bibliothek. Beachten Sie, dass die Dateiendung .dll unter Windows weggelassen werden muss, wenn eine System-Bibliothek geladen werden soll. Unter Linux sieht die Instanziierung der Klasse CDLL folgendermaßen aus:

bibliothek = ctypes.CDLL("libc.so.6")

Unter macOS können Sie die folgende Zeile verwenden:

bibliothek = ctypes.CDLL("libc.dylib")

Nachdem die dynamische Bibliothek eingebunden worden ist, kann die Funktion printf wie eine Methode der CDLL-Instanz aufgerufen werden.

[»]  Hinweis

Die Funktion printf schreibt nicht nach sys.stdout, sondern in den tatsächlichen stdout-Stream des Betriebssystems. Das bedeutet für Sie, dass Sie im oben dargestellten Beispiel nur dann eine Ausgabe sehen, wenn Sie das Programm in einer Python-Shell ausführen. Keine Ausgabe erscheint dagegen beispielsweise in IDLE.

Die normale printf-Funktion erwartet einen bytes-String. Um mit richtigen Strings zu arbeiten, kann ihr Pendant wprintf gerufen werden:

bibliothek.wprintf("Hallo Welt\n")

Behalten Sie bei der Arbeit mit ctypes immer im Hinterkopf, dass es teilweise große Unterschiede zwischen den Datentypen von C/C++ und denen von Python gibt. Es können also nicht alle Funktionen so einfach verwendet werden. Im Laufe dieses Abschnitts werden wir noch eingehend darauf zurückkommen.

[»]  Hinweis

Häufig verlangen C-Funktionen einen Pointer auf einen String als Parameter, über den der String dann manipuliert wird. Beachten Sie unbedingt, dass Sie dann keine Instanzen der Python-Datentypen str oder bytes übergeben dürfen. Das liegt daran, dass diese zu den unveränderlichen Datentypen gehören und auch von einer C-Funktion nicht verändert werden können.

Um einer solchen Funktion dennoch einen beschreibbaren String zur Verfügung zu stellen, verwenden Sie die Funktion create_string_buffer, die wir zusammen mit den anderen Funktionen des Moduls ctypes am Ende dieses Abschnitts besprechen werden.

 
Zum Seitenanfang

37.1.2    Die eigene Bibliothek Zur vorigen ÜberschriftZur nächsten Überschrift

An dieser Stelle wird eine beispielhafte dynamische Bibliothek erstellt, auf die wir in den folgenden Abschnitten zugreifen werden. Die Bibliothek ist in C geschrieben und enthält drei Funktionen mit unterschiedlich komplexer Schnittstelle.

// Berechnet die Fakultaet einer ganzen Zahl
int fakultaet(int n)
{
int i;
int ret = 1;
for(i = 2; i <= n; i++)
ret *= i;
return ret;
}

Die erste Funktion, fakultaet, berechnet die Fakultät einer ganzen Zahl und gibt das Ergebnis ebenfalls in Form einer ganzen Zahl zurück.

// Berechnet die Laenge eines Vektors im R^3
double veclen(double x, double y, double z)
{
return sqrt(x*x + y*y + z*z);
}

Die zweite Funktion, veclen, errechnet die Länge eines dreidimensionalen Vektors. Sie bekommt dazu die drei Koordinaten des Vektors in Form von drei Gleitkommazahlen übergeben und gibt die Länge des Vektors ebenfalls in Form einer Gleitkommazahl zurück.

// Bubblesort
void sortiere(int *array, int len)
{
int i, j, tmp;
for(i = 0; i < len; i++)
{
for(j = 0; j < i; j++)
{
if(array[j] > array[i])
{
tmp = array[j];
array[j] = array[i];
array[i] = tmp;
}
}
}
}

Die dritte, etwas komplexere Funktion, sortiere, implementiert den Bubblesort-Algorithmus[ 160 ](Der Bubblesort-Algorithmus ist einer der einfachsten Sortieralgorithmen. Sein Name ergibt sich daraus, dass die zu sortierenden Elemente wie Luftblasen im Wasser nach oben steigen, bis sie ihre Endposition erreichen. Der Bubblesort-Algorithmus hat eine quadratische Laufzeit und ist damit nicht besonders effizient. ), um ein Array von beliebig vielen ganzen Zahlen aufsteigend zu sortieren. Dazu übergeben wir der Funktion einen Pointer auf das erste Element sowie die Anzahl der Elemente des Arrays.

Im Folgenden gehen wir davon aus, dass der oben stehende Quellcode zu einer dynamischen Bibliothek kompiliert wurde und unter dem Namen bibliothek.dll im jeweiligen Programmverzeichnis der kommenden Beispielprogramme zu finden ist.

 
Zum Seitenanfang

37.1.3    Datentypen Zur vorigen ÜberschriftZur nächsten Überschrift

An dieser Stelle haben wir eine fertige dynamische Bibliothek mit drei Funktionen, die wir jetzt mit ctypes aus einem Python-Programm heraus aufrufen können. Der praktischen Umsetzung dieses Vorhabens stehen jedoch die teilweise inkompatiblen Datentypen von C und Python im Wege. Solange Instanzen der Datentypen int, str, bytes oder NoneType[ 161 ](Wenn die Instanz None an eine C-Funktion übergeben wird, kommt sie dort als NULL-Pointer an. Umgekehrt wird ein von einer C-Funktion zurückgegebener NULL-Pointer in Python zu None. ) übergeben werden, funktioniert der Funktionsaufruf einwandfrei, denn diese Instanzen können eins zu eins nach C übertragen werden. So ist beispielsweise der Aufruf der Funktion fakultaet mit keinerlei Problemen verbunden:

>>> import ctypes
>>> bib = ctypes.CDLL("bibliothek.dll")
>>> print(bib.fakultaet(5))
120

Doch bereits das Übergeben einer float-Instanz scheitert. Für diesen und andere Datentypen von C implementiert das Modul ctypes entsprechende Gegenstücke in Python, deren Instanzen über die Schnittstelle geschickt werden können. Tabelle 37.1 listet alle in ctypes enthaltenen Datentypen sowie ihre Entsprechungen in C und Python auf.

Datentyp (ctypes) Datentyp (C) Datentyp (Python)
c_char char bytes (ein Zeichen)
c_wchar wchar_t str (ein Zeichen)
c_byte char int
c_ubyte unsigned char int
c_short short int
c_ushort unsigned short int
c_int int int
c_uint unsigned int int
c_long long int
c_ulong unsigned long int
c_longlong __int64, long long int
c_ulonglong unsigned __int64,
unsigned long long
int
c_float float float
c_double double float
c_char_p char * bytes, None
c_wchar_p wchar_t * str, None
c_void_p void * int, None

Tabelle 37.1    Datentypen des Moduls ctypes

Diese ctypes-Datentypen können durch Aufruf ihres Konstruktors instanziiert und mit einer Instanz des angegebenen Python-Datentyps initialisiert werden. Über das Attribut value lässt sich der jeweilige Wert eines ctypes-Datentyps verändern.

>>> f = ctypes.c_float(1.337) 
>>> f
c_float(1.3370000123977661)
>>> s = ctypes.c_char_p(b"Hallo Welt")
>>> s.value
b'Hallo Welt'
>>> null = ctypes.c_void_p(None)
>>> null
c_void_p(None)

Um ein Array eines bestimmten Datentyps anzulegen, wird der zugrunde liegende Datentyp mit der Anzahl der Elemente, die er aufnehmen soll, multipliziert. Das Ergebnis ist ein Datentyp, der das gewünschte Array speichert. Im konkreten Beispiel sieht das so aus:

>>> arraytyp = ctypes.c_int * 5
>>> a = arraytyp(1, 5, 2, 1, 9)
>>> a
<__main__.c_int_Array_5 object at 0x7fb0f9549c80>

Einen solchen Array-Typ können wir beispielsweise der Funktion sortiere übergeben, die ein Array von ganzen Zahlen sortiert:

import ctypes
bib = ctypes.CDLL("bibliothek.dll")
arraytyp = ctypes.c_int * 10
a = arraytyp(0,2,5,2,8,1,4,7,3,8)
print("Vorher: ", [i for i in a])
bib.sortiere(a, 10)
print("Nachher: ", [i for i in a])

Die Ausgabe dieses Beispielprogramms lautet:

Vorher:  [0, 2, 5, 2, 8, 1, 4, 7, 3, 8]
Nachher: [0, 1, 2, 2, 3, 4, 5, 7, 8, 8]

Gelegentlich ist es notwendig, die Größe eines ctypes-Datentyps zu bestimmen. Das ist der Platz in Byte, den der korrelierende C-Datentyp belegt. Dazu implementiert ctypes die Funktion sizeof, die den gleichnamigen Operator von C auf Python abbildet. Zurückgegeben wird die Größe der übergebenen Instanz bzw. des übergebenen ctypes-Datentyps in Byte.

>>> ctypes.sizeof(ctypes.c_int)
4
>>> ctypes.sizeof(ctypes.c_char)
1
>>> ctypes.sizeof(ctypes.c_double(1.7))
8
 
Zum Seitenanfang

37.1.4    Schnittstellenbeschreibung Zur vorigen ÜberschriftZur nächsten Überschrift

Die Funktion veclen bekommt drei Gleitkommazahlen übergeben, interpretiert diese als dreidimensionalen Vektor und gibt dessen Länge zurück.

from ctypes import CDLL, c_double
bib = CDLL("bibliothek.dll")
print(bib.veclen(c_double(1.5), c_double(2.7), c_double(3.9)))

Wird dieser Code ausgeführt, erhalten wir mit -285338777 einen Wert zurück, der nun wirklich nicht der gesuchten Vektorlänge entspricht. Das liegt daran, dass eine dynamische Bibliothek keine Informationen über die Funktionsschnittstellen speichern muss. Bei der Programmierung mit C bzw. C++ wird daher zusätzlich zur dynamischen Bibliothek noch ein Header benötigt, der die Schnittstellen definiert.

Beim Laden einer dynamischen Bibliothek mit ctypes ist diese Header-Information nicht verfügbar, weswegen für alle Funktionen der Rückgabetyp int angenommen wird, was natürlich in vielen Fällen falsch ist. Die von veclen zurückgegebene Gleitkommazahl wurde also aus Unwissenheit als ganze Zahl interpretiert und entsprechend ausgegeben. Damit dies in Zukunft nicht mehr passiert, können wir über das Attribut restype eines Funktionsobjekts den Datentyp des Rückgabewertes explizit angeben:

from ctypes import CDLL, c_double
bib = CDLL("bibliothek.dll")
bib.veclen.restype = c_double
print(bib.veclen(c_double(1.5), c_double(2.7), c_double(3.9)))

Die Ausgabe von 4.97493718553 zeigt, dass der Rückgabewert jetzt korrekt interpretiert wird.

Neben den Rückgabetypen fehlen in dynamischen Bibliotheken auch Informationen über die Parameter von Funktionen. Solange wie in den vorangegangenen Beispielen Instanzen der passenden Typen übergeben werden, fällt dies nicht weiter ins Gewicht. Ähnlich wie im Falle der Rückgabetypen kann das Modul ctypes ohne weitere Information aber nicht prüfen, ob die übergebenen Datentypen stimmen. Würden Sie beispielsweise im oben dargestellten Programm anstelle des Datentyps c_double Instanzen des Datentyps c_float übergeben, dann würde bereits bei der Parameterübergabe ein Fehler in der Interpretation der Daten passieren, der letztlich in einen falschen Rückgabewert mündete.

Das Modul ctypes bietet Ihnen an, über das Attribut argtypes einer Funktion die Parameterschnittstelle festzulegen. Wenn das gemacht wird, werden übergebene Instanzen eines falschen Datentyps in den korrekten Datentyp konvertiert, oder es wird, wenn dies nicht möglich ist, eine ctypes.ArgumentError-Exception geworfen.

Im folgenden Programm wird die vollständige Schnittstelle der Funktion veclen vorgegeben:

from ctypes import CDLL, c_double
bib = CDLL("bibliothek.dll")
bib.veclen.restype = c_double
bib.veclen.argtypes = [c_double, c_double, c_double]
print(bib.veclen(c_double(1.5), c_double(2.7), c_double(3.9)))
[»]  Hinweis

Zwar werden unter Verwendung des Moduls ctypes in vielen Fehlerfällen Exceptions geworfen, beispielsweise wenn zu viele, zu wenige oder die falschen Parameter übergeben werden, doch Sie sollten sich immer vergegenwärtigen, dass Sie mit ctypes viele Schutzmechanismen von Python umgehen und möglicherweise direkt im Speicher arbeiten. Es ist also durchaus möglich, unter Verwendung von ctypes den Python-Interpreter zum Absturz zu bringen.

 
Zum Seitenanfang

37.1.5    Pointer Zur vorigen ÜberschriftZur nächsten Überschrift

Ein zentrales Konzept der Programmiersprache C sind Pointer. Ein Pointer ist ein Zeiger auf eine Stelle im Arbeitsspeicher. Der Typ eines Pointers legt fest, wie der Inhalt der Speicherstelle interpretiert wird. Das Modul ctypes enthält einige Funktionen, die es ermöglichen, Pointer zu verwenden.

ctypes.addressof(obj)

Gibt die Adresse der Instanz obj eines ctypes-Datentyps zurück.

ctypes.byref(obj, [offset])

Erzeugt einen Pointer auf die Instanz obj eines ctypes-Datentyps. Der zurückgegebene Pointer kann einer C-Funktion übergeben werden. Ein für offset übergebenes ganzzahliges Offset wird auf den Pointer aufaddiert.

>>> x = ctypes.c_int()
>>> bib.scanf(b"%d", ctypes.byref(x))
27
1
>>> x
c_int(27)

Im Beispiel wurde die Funktion scanf der Standard C Library verwendet, um eine ganze Zahl vom Benutzer einzulesen. Dazu muss ein Pointer auf eine int-Variable übergeben werden. Dieser wird über die Funktion byref erzeugt. Wie Sie sehen, ist die eingegebene 27 tatsächlich in die Variable x geschrieben worden. Bei der 1 handelt es sich um den Rückgabewert von scanf.

ctypes.cast(obj, type)

Die Funktion cast bildet den Cast-Operator von C in Python ab. Die Funktion gibt eine neue Instanz des ctypes-Pointer-Datentyps type zurück, die auf die gleiche Speicherstelle verweist wie obj.

>>> s = ctypes.c_char_p(b"Hallo Welt")
>>> vp = ctypes.cast(s, ctypes.c_void_p)
>>> vp.value
140011580525696
>>> ctypes.cast(vp, ctypes.c_char_p).value
b'Hallo Welt'

Hier wurde ein char-Pointer in einen void-Pointer und dieser wieder zurück in einen char-Pointer gecastet.

ctypes.string_at(address, [size])

Diese Funktion gibt den String zurück, der an der Speicheradresse address steht. Sollte der String im Speicher nicht null-terminiert sein, kann über den Parameter size die genaue Länge des Strings übergeben werden. Analog existiert die Funktion wstring_at für Unicode-Strings.

Für address muss eine ganze Zahl übergeben werden, die sinnvollerweise mit addressof geholt und verändert wurde.

 
Zum Seitenanfang

37.1.6    Strings Zur vorigen ÜberschriftZur nächsten Überschrift

Im Gegensatz zu Python sind Strings in C veränderliche Datentypen. Das liegt daran, dass sie als Pointer auf ein char-Array implementiert sind. Funktionen in C nehmen häufig einen solchen Pointer entgegen und manipulieren den String direkt im Speicher. Damit solche Schnittstellen mit ctypes verwendet werden können, existiert die Funktion create_string_buffer.

ctypes.create_string_buffer(init_or_size, [size])

Diese Funktion erzeugt einen veränderlichen String-Buffer, in den aus einer C-Funktion heraus geschrieben werden kann. Zurückgegeben wird ein Array von c_char-Instanzen. Für den ersten Parameter init_or_size können Sie entweder eine ganze Zahl übergeben, die die Länge des zu erzeugenden Buffers enthält, oder einen String, mit dem der Buffer initialisiert werden soll. Im Falle eines Strings wird der Buffer ein Zeichen größer gemacht, als der String lang ist. In dieses letzte Zeichen wird der Terminator »\0« geschrieben.

Wenn Sie für init_or_size einen String übergeben haben, können Sie über den Parameter size die Größe des Buffers festlegen, sofern nicht die Länge des Strings genommen werden soll.

>>> s = ctypes.create_string_buffer(20)
>>> bib.sprintf(s, b"Die Zahl ist: %d", 12)
16
>>> s.value
b'Die Zahl ist: 12'

Im Beispiel wurde ein String-Buffer der Länge 20 erzeugt und mittels sprintf ein formatierter Text hineingeschrieben.

 


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