Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Lesedauer 14 Min.

Bibliotheken nutzen

Nach der Behandlung weiterer Daten-Container unternimmt dieser Teil des Python-Umsteiger-Kurses einen Abstecher in die Welt der Python-Bibliotheken.
© dotnetpro
Dieser Artikel zeigt mithilfe weiterer einfacher Beispiele, wie die Programmiersprache Python funktioniert und eingesetzt werden kann. Neben weiteren Daten-Containern wird es in erster Linie um die Benutzung fertiger Standard-Bibliotheken gehen. Das in der Python-Community am häufigsten verwendete Modul ist sicherlich numpy, das unter anderem auch eine sehr leistungsfähige Array-Klasse enthält.

Dictionaries

Ein Dictionary funktioniert ähnlich wie eine Python-Liste [1], ist allerdings generischer. Während bei einer Liste der Zugriffsindex immer eine ganze Zahl (Integer) sein muss, können Sie mit einem Dictionary fast jeden Datentyp als Index verwenden.Solche Dictionarieskennen wir zum Beispiel vom Dateisystem der Festplatte: Der Index eines Eintrags ist der Datei­name in Form eines Strings. Dieser „zeigt“ dann eindeutig auf den Dateiinhalt. Man spricht dann auch von Mapping zwischen einer Reihe von Indizes (Schlüsseln) und einem dazugehörigen Wert (Schlüssel-Wert-Paare).Man kann sich ein Dictionary auch als Wörterbuch vorstellen, wie hier im ersten Beispiel gezeigt wird. Zuerst wird ein leeres Dictionary erstellt:
de2en = dict() 
print(<span class="hljs-name">de2en</span>) 
Dictionaries werden bei der Ausgabe mit geschweiften Klammern versehen, ein leeres Dictionary wird also durch {} dargestellt. Nun können Elemente in das Dictionary eingefügt werden. Hier wird die bereits bekannte Index-Schreibweise benutzt:
de2en[<span class="hljs-string">"eins"</span>] = <span class="hljs-string">"one"</span> 
de2en[<span class="hljs-string">"zwei"</span>] = <span class="hljs-string">"two"</span> 
de2en[<span class="hljs-string">"drei"</span>] = <span class="hljs-string">"three"</span> 
Der Index des Dictionarys ist, ebenso wie der Wert, vom Typ String. Die Ausgabe liefert folgendes Ergebnis:
{<span class="hljs-string">'eins'</span>: <span class="hljs-string">'one'</span>, <span class="hljs-string">'zwei'</span>: <span class="hljs-string">'two'</span>, <span class="hljs-string">'drei'</span>: <span class="hljs-string">'three'</span>} 
Hier kann man die Zuordnung von Schlüssel und Wert genau erkennen. Wenn nun ein deutsches Wort ins Englische übersetzt werden soll, wird das ganz einfach mit folgendem Python-Befehl realisiert:
<span class="hljs-function"><span class="hljs-title">print</span><span class="hljs-params">(de2en[<span class="hljs-string">"zwei"</span>])</span></span> 
Das Ausgabeergebnis ist in diesem Fall natürlich two.Wenn man einen Schlüssel benutzt, der nicht im Dictio­nary enthalten ist, gibt der Python-Interpreter die Fehlermeldung KeyError aus. Probieren Sie es einmal aus. Die Eingabe des Befehls
<span class="hljs-function"><span class="hljs-title">print</span><span class="hljs-params">(de2en[<span class="hljs-string">"vier"</span>])</span></span> 
liefert <span class="hljs-keyword">die </span>entsprechende Fehlermeldung. 
In der Praxis müssen häufig sogenannte Histogramme erstellt werden. Diese stellen die Anzahl bestimmter Werte in einem Balken mit einer entsprechenden Höhe dar. Man kann sehr gut ein Dictionary benutzen, um zum Beispiel die Anzahl der Buchstaben in einem Satz zu zählen. Buchstaben, die nicht vorkommen, benötigen auch keinen Speicherplatz im Dictionary. Die Vorgehensweise zeigt Listing 1.
Listing 1: Ein Histogramm
# Histogramm-Funktion&lt;br/&gt;def histogramm(data):&lt;br/&gt;    balken = dict()&lt;br/&gt;    data = data.upper()&lt;br/&gt;    &lt;br/&gt;    for c &lt;span class="hljs-keyword"&gt;in&lt;/span&gt; data:&lt;br/&gt;        &lt;span class="hljs-keyword"&gt;if&lt;/span&gt; c not &lt;span class="hljs-keyword"&gt;in&lt;/span&gt; balken:&lt;br/&gt;            # Erste Eintragung&lt;br/&gt;            balken[c] = &lt;span class="hljs-number"&gt;1&lt;/span&gt;&lt;br/&gt;        else:&lt;br/&gt;            # Weitere Buchstaben&lt;br/&gt;            balken[c] += &lt;span class="hljs-number"&gt;1&lt;/span&gt;&lt;br/&gt;    &lt;br/&gt;    return balken&lt;br/&gt;&lt;br/&gt;### Main-Methode ###############&lt;br/&gt;satz = &lt;span class="hljs-string"&gt;"Das ist ein schoenes Pythonprogramm"&lt;/span&gt;&lt;br/&gt;diagramm = histogramm(satz)&lt;br/&gt;&lt;br/&gt;# Einfache Standardausgabe&lt;br/&gt;print(diagramm) &lt;br/&gt;&lt;br/&gt;# Ende des Programms 
Die Histogramm-Funktion lässt sich mithilfe eines Dictionarys sehr einfach und speicherschonend programmieren. In der Funktion wird zuerst ein leeres Dictionary erstellt. Da wir in unserer Histogramm-Funktion die Anzahl der vorkommenden Buchstaben in einem String zählen wollen, konvertieren wir diesen zunächst komplett in Großbuchstaben, um die Ergebnisdatenmenge etwas zu reduzieren.Nun kann man mit einer einfachen for-Schleife über die Buchstaben im String data iterieren. Mit einem if-Statement wird geprüft, ob der Buchstabe schon im Dictionary vorhanden ist. Wenn dies nicht der Fall ist, wird ein neues Element mit dem Buchstaben als Schlüssel im Dictionary erstellt und mit dem Integer-Wert 1 initialisiert. In allen anderen Fällen wird der Wert zum jeweiligen Schlüssel einfach um eins erhöht. Als Ergebnis gibt die Funktion das erzeugte Dictionary mit den Zählwerten zurück.Die Main-Methode des Beispiels definiert zunächst einen String und ruft dann die Histogramm-Funktion auf. Im einfachsten Fall gibt man dann das zurückgegebene Dictionary mit einem print-Befehl aus. Das Ergebnis ist allerdings nicht sonderlich gut lesbar:
{'D': 1, 'A': 2, 'S': 4, ' ': 4, 'I': 2, 'T': 2, 'E': 3, 
  'N': 3, 'C': 1, 'H': 2, 'O': 3, 'P': 2, 'Y': 1, 'R': 
  2, 'G': 1, 'M': 2} 
Mit zwei weiteren Python-Zeilen kann man die Situation aber wesentlich verbessern: Es wäre doch schön, wenn die Buchstaben in alphabetischer Reihenfolge mit der jeweiligen Anzahl dahinter ausgegeben würden. Dazu muss das Dictionary nach den Schlüsselwerten sortiert werden. Im Moment ist die Reihenfolge durch das erste Auftreten eines Buchstabens bestimmt. Dazu wird die Zeile mit dem print-Befehl einfach auskommentiert und die folgenden Zeilen eingefügt:
# Einfache Standardausgabe 
#print(diagramm) 

# Sortierte Ausgabe 
for index in sorted (diagramm.keys()):  
    print(index, " - ", diagramm[index])      

# Ende des Programms 
In der neuen for-Schleife wird der Inhalt des Dictionarys nach den Schlüsseln (Eigenschaft keys() im Dictionary diagramm) sortiert und mit dem neuen print-Befehl ausgegeben.Listen-Objekte können auch als Werte in einem Dictionary enthalten sein:
list1 = ["a", "b", "c"] 
list2 = ["x", "y", "z"] 
d = dict() 
d[1] = list1 
d[2] = list2 
# Ausgabemöglichkeiten: 
# Gesamtes Dictionary 
print(d) 
# Eine Liste aus dem Dictionary 
print(d[1]) 
# Ein Element aus einer Liste 
print(d[1][2]) 
Der erste print-Befehl gibt das gesamte Dictionary in der bekannten Weise aus:
{1: ['a', 'b', 'c'], 2: ['x', 'y', 'z']} 
Der zweite Ausgabebefehl gibt eine komplette Liste aus. Die Liste wird durch den Schlüssel (hier: 1) festgelegt. Der letzte print-Befehl zeigt ein Element aus der gewählten Liste an. Der erste Index (hier wieder: 1) gibt den Schlüssel für die Liste im Dictionary an. Der zweite Index in eckigen Klammern (hier: 2) gibt die Position des Elements in der jeweiligen Liste (beginnend mit 0) an.

Globale Variablen

An dieser Stelle möchte ich einen kurzen Ausflug in die Welt der globalen Variablen unternehmen. Solche globalen Variablen werden außerhalb aller Funktionen deklariert und gehören zu einem besonderen Frame mit dem Namen __main__. Auf diese Variablen kann man von jeder Funktion aus zugreifen. Während alle lokalen Variablen nach dem Funktionsaufruf verschwinden, bleiben die globalen Variablen erhalten und können später aus einer anderen Funktion heraus benutzt werden.Hierbei gibt es allerdings einige Regeln, die unbedingt beachtet werden müssen.
meinWert = 42    # globale Variable 

def meineFunk(): 
    print(meinWert) 

### Hier beginnt das main-Frame 
meineFunk() 
print(meinWert) 
### Ende des main-Frames 
Wenn Sie dieses kleine Programm laufen lassen, wird die Zahl 42 zweimal im Konsolenfenster ausgegeben. Die Variable meinWert wird im main-Frame deklariert und initialisiert. Danach wird nur noch lesend auf die Variable zugegriffen.Das Beispiel soll nun so geändert werden, dass die aufgerufene Funktion die globale Variable meinWert ändert:
meinWert = 42    # globale Variable 

def meineFunk2(): 
    meinWert = 100 
    print(meinWert) 

### Hier beginnt das main-Frame 
print(meinWert) 
meineFunk2() 
print(meinWert) 
### Ende des main-Frames 
In meinem Beispiel habe ich eine neue Funktion meineFunk2 definiert, welche die Variable meinWert auf 100 setzt und diese dann ausgibt.In der main-Funktion wird zunächst die globale Variable ausgegeben (42), dann wird die Funktion aufgerufen und die Variable meinWert wird auf 100 gesetzt und ausgegeben. Wir verlassen nun die Funktion und befinden uns wieder in der main-Methode. Nun wird die globale Variable meinWert erneut ausgegeben. Wir erhalten nun aber überraschenderweise wieder den Wert 42.Was ist passiert? Es ist eigentlich ganz einfach: In der Funktion meineFunk2 hat der Python-Interpreter eine neue lokale Variable meinWert erstellt und mit 100 initialisiert. Am ­Ende der Funktion wurde diese Variable dann weggeräumt. In der Funktion selbst wurde also gar nicht auf die globale Variable zugegriffen.Um das gewünschte Verhalten für die globale Variable meinWert zu erhalten, ist nur eine kleine, aber wichtige Änderung erforderlich:
# Korrekte globale Variable 
meinWert = 42    # globale Variable 

def meineFunk3(): 
    global meinWert 
    meinWert = 100 
    print(meinWert) 

### Hier beginnt das main-Frame 
print(meinWert) 
meineFunk3() 
print(meinWert) 
### Ende des main-Frames 
Beachten Sie in diesem Beispiel die Zeile mit der global-­Deklaration in der Funktion meineFunk3. Dort wird für den Python-Interpreter festgelegt, dass in der Funktion immer die globale Variable meinWert benutzt werden soll.Als Ergebnis erhalten wir die folgende Ausgabe im Konsolenfenster:
42 
100 
100 
Das folgende Beispiel mit einer globalen Variablen liefert übrigens beim Aufruf der Funktion eine Fehlermeldung:
count = 0 

def falscheFunktion(): 
    count = count + 1  # Fehler 
Hier haben wir die Variable count, die im main-Frame angelegt wird. Auf diese Variable count wird nun in der Funktion lesend zugegriffen, sie ist aber noch gar nicht angelegt und initialisiert. Dies führt dann zur Fehlermeldung UnboundLocalError: ...Nun kommen wir wieder auf Listen und Dictionaries, also auf sogenannte veränderbare Typen, zu sprechen. Diese Container enthalten Daten, die zur Laufzeit des Programms geändert werden können. Dabei werden lediglich die Daten im Container verändert, es wird jedoch kein neuer Container erzeugt.Im folgenden Beispiel wird zunächst ein Dictionary im main-Frame erstellt und zwei Daten-Elemente werden initialisiert. Die Funktion fügt ein weiteres Element in den Container ein. Diese Operation funktioniert einwandfrei, weil nur der Inhalt des existierenden Dictionarys geändert wird.
# Ein globales Dictionary 
meinDict = {"a":0, "b":1} 

def meineFunkDict(): 
    # Existierendes Dictionary ändern 
    meinDict["a"] = 2 

### main-Frame ### 
print(meinDict) 
meineFunkDict() 
print(meinDict) 
### Ende main-Frame ### 
Die Ausgabe des Beispielprogramms zeigt das ursprüngliche und das erweiterte Dictionary:
{'a': 0, 'b': 1} 
{'a': 0, 'b': 1, 'c': 2} 
Wenn Sie jedoch der Variablen meinDict ein neues Dictio­nary-Objekt zuweisen wollen, muss wieder das Schlüsselwort global verwendet werden:
# Ein globales Dictionary 
meinDict = {"a":0, "b":1} 

def meineFunkDict2(): 
    global meinDict 
    # Neues Dictionary zuweisen 
    meinDict = dict() 

### main-Frame ### 
print(meinDict) 
meineFunkDict2() 
print(meinDict) 
### Ende main-Frame ### 

Tupel

Ein Tupel ist eine Reihenfolge (Sequenz) von Werten. Die Werte können von beliebigem Typ sein. Die einzelnen Elemente in einem Tupel werden über einen Integer-Index angesprochen. Tupel und Listen sind sich sehr ähnlich, es gibt allerdings einen entscheidenden Unterschied: Tupel sind nicht veränderbar.Ein Tupel wird durch eine kommaseparierte Liste von Werten erstellt:
meinTupel = 'a', 'b', 'c' 
Es ist zwar nicht erforderlich, aber wegen der besseren Lesbarkeit werden Tupel in runden Klammern geschrieben. Dies gilt auch für die Ausgabe von Tupeln im Konsolenfenster:
nochEinTupel = ( 1, 2.4, 'abc' ) 
print(nochEinTupel) 
print(nochEinTupel[1]) 
Hier wird ein Tupel mit unterschiedlichen Typen erzeugt. Der erste print-Befehl gibt das gesamte Tupel-Objekt in runden Klammern aus. Der zweite print-Befehl listet dagegen nur das zweite Element des Tupels. Wie bei Listen wird auch der Index bei Tupeln nullbasierend angewendet.Bei der Initialisierung von Tupel-Objekten muss man etwas aufpassen, denn die Zeile
xxx = ( 'abc' ) 
liefert kein Tupel, sondern einen String. Um ein Tupel mit nur einem Element zu erstellen, müssen Sie nach dem Wert ein Komma einfügen:
xxx = ( 'abc', ) 
yyy = 'abc',      # geht auch 
Ein leeres Tupel wird durch den folgenden Python-Befehl ­erzeugt:
zzz = tuple() 
Wenn man als Argument bei der Tupel-Initialisierung ein Sequenz-Objekt (String, Liste oder Tupel) übergibt, erhält man ein neues Tupel mit sämtlichen einzelnen Elementen dieser Sequenz:
t = tuple('abcdefghij') 
print(t) 
Dieses kleine Code-Snippet liefert folgende Ausgabe:
('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j') 
Nun kann man mit Integer-Indizes auf die einzelnen Elemente im Tupel zugreifen.
print(t[0])      # a 
print(t[-1])    # j 
print(t[2:4])    # ('c', 'd') 
print(t[7:])    # ('h', 'i', 'j') 
print(t[:2])    # ('a', 'b') 
Bei der Indizierung von Tupel-Elementen werden also die gleichen Regeln angewendet, die auch für Listen gelten.Wenn Sie jedoch versuchen, ein Element eines Tupels zu verändern, erhalten Sie die Fehlermeldung TypeError: ...:
t[1] = 'B'  # Fehler: TypeError 
Man kann die Elemente eines Tupels also nicht ändern, aber man kann ein bereits existierendes Tupel durch ein anderes ersetzen:
# Neues Tupel erstellen 
tup = ('a', 'b', 'c') 
print(tup) 
# Neues Tupel hinten erweitern 
tup = tup + ('Z',) 
print(tup) 
# Neues Tupel vorne erweitern 
tup = ('A',) + tup 
print(tup) 
# Noch ein neues Tupel aus dem alten erzeugen 
tup = tup[:2] + tup[3:] 
print(tup) 
Das Beispielprogramm liefert folgende Ausgaben:
('a', 'b', 'c') 
('a', 'b', 'c', 'Z') 
('A', 'a', 'b', 'c', 'Z') 
('A', 'a', 'c', 'Z') 
Im Zusammenhang mit Tupeln gibt es auch noch eine sehr interessante Lösung. Sie haben bestimmt schon einmal zwei Variablen mithilfe einer temporären Variablen ausgetauscht:
# Es gibt zwei Variablen a und b 
temp = a 
a = b 
b = temp 
Hier kann man in Python die sogenannte Tupel-Zuweisung benutzen, die wesentlich eleganter ist:
# Es gibt wieder zwei Variablen a und b 
print(a, b) 
a, b = b, a  # Hier Tupel-Zuweisung 
print(a, b) 
Der Code in der Zeile zwischen den beiden print-Befehlen vertauscht tatsächlich die Inhalte der beiden Variablen. In diesem Fall ist die linke Seite vom Gleichheitszeichen ein Tupel von Variablen, während auf der rechten Seite ein Tupel von Ausdrücken zu finden ist. Jeder Wert wird dann der entsprechenden Variablen auf der linken Seite zugewiesen, wobei aber die Werte auf der rechten Seite vor der Zuweisung erst komplett ausgewertet werden. Bei dieser Operation ist jedoch zu beachten, dass auf der linken und der rechten Seite des Gleichheitszeichens immer gleich viele Elemente vorhanden sein müssen.Die Benutzung von Tupeln ist auch im Zusammenhang mit Funktionen sehr interessant. Normalerweise können Funk­tionen nur einen Wert mit dem return-Befehl zurückgeben. Wenn man aber ein Tupel-Objekt aus der Funktion zurückgibt, kann man damit auch mehrere Ergebniswerte (auch unterschiedlichen Typs) weitergeben.Im folgenden Beispiel soll in einer Python-Funktion der ­y-Wert einer Funktion an der Stelle x berechnet werden. ­Außerdem sollen auch noch die Werte der ersten und zweiten Ableitung ermittelt werden. Alle drei Ergebnisse sollen mit return an die aufrufende Funktion transferiert werden:
def func(x): 
    # Die Funktion y(x) 
    y = 3*x**3 + x**2 + 2*x – 2 
    # Die 1. Ableitung y'(x) 
    y1 = 9*x**2 + 2*x + 2 
    # Die 2. Ableitung y''(x) 
    y2 = 18*x + 2 
    # Rückgabe der drei Ergebniswerte 
    return y, y1, y2 

### Hauptprogramm ### 
x = 1.5 

# Rückgabe in ein Tupel 
result = func(x) 
print(result) 
print(type(result)) 

# Rückgabe in einzelne Variablen 
res0, res1, res2 = func(x) 
print("Funktionswert: ", res0) 
print("1. Ableitung : ", res1) 
print("2. Ableitung : ", res2) 
# Ende Hauptprogramm 
Zunächst definieren wir die Berechnungsfunktion func mit dem Parameter x. An dieser Stelle sollen die Funktion und die beiden Ableitungen berechnet werden. Das ist alles sehr unspektakulär.Erst die Rückgabe der Ergebnisse ist interessant: Es werden drei Variablen zurückgegeben, die Python zu einem Tupel zusammenfasst. Dies kann man im ersten func-Aufruf im Hauptprogramm sehr schön sehen, denn die Ausgabe des result-Objekts im print-Befehl zeigt die drei Ergebniswerte in runden Klammern – also ein Tupel-Objekt.Durch die Ausgabe in der nachfolgenden Zeile wird dies bestätigt:
(13.375, 25.25, 29.0) 
&lt;class 'tuple'&gt; 
Die einzelnen Werte im Tupel werden durch Indizierung mit eckigen Klammern angesprochen. Die folgende Zeile gibt den Wert der ersten Ableitung aus:
print(result[1]) 
Der zweite Aufruf von func benutzt dagegen auf der linken Seite des Gleichheitszeichens drei Variablen (res0, res1 und res2), in welche die einzelnen Werte im Rückgabe-Tupel kopiert werden. Diese können dann ganz normal weiterverwendet werden.Es gibt aber noch ein weiteres interessantes Tupel-Feature in Python. Man kann Tupel benutzen, um Funktionen mit variabler Parameteranzahl zu programmieren. Bei einer solchen Funktion werden alle Parameterwerte beim Aufruf zu einem Tupel zusammengefasst. Im Kopf der Funktion wird nur ein Parameter deklariert, dem ein * vorangestellt wird. Das folgende Beispiel zeigt eine spezielle Ausgabefunktion, die beliebig viele Parameter im Konsolenfenster ausgibt:
# Datenübergabe vieler Werte als Tupel 
def superPrint(*data): 
    # 1: Ausgabe als Tupel 
    print(data) 
    
    # 2: Als einzelne Werte 
    for x in data: 
        print(x) 
In der Funktion superPrint werden zwei Ausgabevarianten gezeigt: die einfache Ausgabe als Tupel und eine Auflistung der Werte in einzelnen Zeilen. Entscheidend ist hier der Parameter *data in der Kopfzeile der Funktion. Die Funktion kann nun mit einer unterschiedlichen Parameteranzahl aufgerufen werden:
# Mit konstanten Werten 
superPrint(1, 2.4, 'abc', 4, -567.567) 
superPrint('a', 'b', 'c') 
# Mit Variablen 
x = 1 
y = 4.5678 
superPrint(x, y) 
Die relationalen Operatoren (<, >, <=, >= …) kann man ebenfalls mit Tupeln und anderen Sequenz-Containern verwenden. Python beginnt damit, das erste Element der beiden Sequenzen zu vergleichen. Falls diese gleich sind, geht es mit den nächsten Sequenz-Elementen weiter, bis unterschiedliche Elemente gefunden werden. Nun wird der jeweilige relationale Operator angewendet. Wenn die Bedingung nicht erfüllt ist, werden die nachfolgenden Elemente ignoriert. Hier einige Beispiele; das Ergebnis steht jeweils im Kommentar dahinter:
(0, 1, 2) &lt; (0, 3, 4)    # True 
(0, 1, 2) &gt; (0, 3, 4)    # False  
(0, 1, 2) == (0, 1, 2)  # True 
(1, 2, 3) == (1, 2)      # False 
(1, 2, 3) &lt; (1, 3, 1)    # True 
(1, 2, 3) &lt; (1, 2, 1)    # False 

Ein kleiner Ausflug: Bibliotheken, Arrays und numpy

Python ist eine Skriptsprache, die zur Laufzeit interpretiert wird. Das bedeutet jedoch, dass die Ausführung von Python-Code langsamer abläuft, als zum Beispiel ein gleichartiger Laufzeit-Code in C, C++ oder C# ausgeführt wird. Dafür muss der Code in Python nicht aufwendig durch einen Compiler übersetzt und gebunden werden.Ein wesentlicher Vorteil bei der Programmierung mit Python ist jedoch, dass viele Bibliotheken tatsächlich in der Programmiersprache C geschrieben wurden und dadurch sehr performant sind. Das ist im Prinzip auch der Weg, den man bei größeren Python-Projekten geht: möglichst wenig Python-Code, dafür aber möglichst viele Aufrufe in die schnellen Python-Bibliotheken.Eine wichtige und sehr schnelle Bibliothek ist numpy. An dieser Stelle wollen wir nun lernen, wie solche vorgefertigten Bibliotheken in Python aufgerufen werden.Wenn Sie bereits mit anderen Programmiersprachen zu tun hatten, haben Sie sich vielleicht gewundert, warum es in ­Python Listen, Dictionaries und Tupel, aber keine einfachen Arrays gibt. Dieses Rätsel wird nun aufgelöst: Es gibt auch in Python Arrays, und diese befinden sich in der Bibliothek numpy. Allerdings steht hier die Bibliotheksbenutzung im Vordergrund.Die Python-Arrays möchte ich Ihnen im Rahmen dieses Artikels nur mit einigen wenigen Beispielen erläutern. Wenn Sie sich damit intensiver beschäftigen wollen, möchte ich ­Ihnen die Artikelserie „Mathematik mit Python“ [2][3][4][5] nahe­legen. Dort werden die Möglichkeiten der Python-­Arrays ausführlich mit vielen Beispielen und anderen Bibliotheken vorgestellt.Die folgenden kleinen Beispielprogramme können Sie nur dann korrekt ausführen, wenn Sie die Bibliothek numpy in­stalliert haben. Sollte das nicht der Fall sein, können Sie die Installation mit folgendem Befehl aus dem Kommandozeilenfenster nachholen:
pip install numpy 
Wenn man eine externe Bibliothek mit Python benutzen möchte, muss sie zunächst mit einem import-Befehl bereitgestellt werden. So gibt es zum Beispiel die Bibliothek math, welche einfache mathematische Standardfunktionen enthält. Dazu zählen Wurzelberechnung, Sinus, Cosinus, Logarithmen-Funktionen und viele andere mehr. Hier ein vollständiges Beispiel: 
# Bibliothek math benutzen 
import math 

a = math.sqrt(2.0) # Wurzel 
print(a) 
a = math.exp(3.0)  # Exponent berechnen 
print(a) 
a = math.sin(1.2)  # Sinus 
print(a) 
Zunächst wird in der zweiten Zeile des Beispiels die Bib­liothek math für die Benutzung importiert. In den folgenden Zeilen werden verschiedene Funktionen aus der Library aufgerufen (sqrt, sin, exp ...). In dieser Variante müssen Sie den Bibliotheksnamen (also: math) beim Aufruf einer Funktion immer mit einem Punkt voranstellen.Möchte man den Bibliotheksnamen vermeiden, gibt es noch eine andere Variante für den import-Befehl:
# Funktion sqrt aus Bibliothek math benutzen 
from math import sqrt, log 

a = sqrt(2.0) 
print(a) 
a = log(0.5) 
print(a) 
In diesem Beispiel werden die Funktionen sqrt und log direkt zur Verfügung gestellt und können ohne den Bibliotheks­namen angesprochen werden.Und nun kommt noch eine dritte Variante für den import-Befehl. Hierbei kann man dem Bibliotheksmodul einen neuen Namen geben. Das kann zum Beispiel dann sinnvoll sein, wenn es schon eine Variable mit dem Namen math gibt:
# Variable math – keine gute Idee 
math = 15 
# Bibliothek math als mlib nutzen 
import math as mlib 

# Wurzel berechnen 
a = mlib.sqrt(math) 
print(a) 
Natürlich sollte man solchen Code vermeiden, da er sehr schwer zu lesen ist und bei Änderungen schnell zu Fehlern führt. Aber der Code funktioniert. Wenn die Variable math ini­tialisiert ist und danach die Bibliothek importiert wird (importmath), kann auf die ursprüngliche Variable nicht mehr zugegriffen werden.

Arrays aus der Bibliothek numpy

Die Leser, die bereits andere Programmiersprachen kennen, werden an dieser Stelle vielleicht die normalen ein- oder mehrdimensionalen Arrays vermissen. Nun ja, die kommen jetzt dran, aber dazu benötigen wir die Bibliothek numpy.Diese Standard-Arrays möchte ich hier jedoch nur sehr kurz besprechen.Im ersten Beispiel sollen zwei Arrays angelegt und in einer Schleife mit Daten befüllt werden. In einer zweiten Schleife sollen die Daten im Konsolenfenster ausgegeben werden.
import numpy as np 
import math 

t = np.empty(201) 
s = np.empty(201) 

# Schleife von 0 bis einschl. 200 
for i in range(201): 
    t[i] = (i - 1) * 0.01 
    s[i] = 1.0 + math.sin(2 * math.pi * t[i]) 

# Ausgabe der berechneten Daten (teilweise) 
for k in range(10, 20): 
    print(t[k], s[k]) 
Am Anfang des Programms werden die benötigten Bibliotheken importiert: numpy und math. Hier wird numpy unter dem Alias-Namen np bereitgestellt. Diese Vorgehensweise werden Sie in vielen Python-Programmen so vorfinden.Danach werden zwei leere Arrays t und s erstellt, die jeweils 201 Elemente groß sind. Die Arrays sind jedoch nicht wirklich leer, denn sie allokieren den Speicher für 201 Double-­Precision-Zahlen (8 Byte). Dieser Speicherbereich wird aber nicht mit Nullen initialisiert. Das bedeutet, dass die folgenden Codezeilen
import numpy as np 
x = np.empty(10) 
print(x) 
mehr oder weniger verrückte Zahlen im Konsolenfenster ausgeben. Man muss also unbedingt darauf achten, dass man nicht auf uninitialisierte Array-Elemente zugreift.Im Beispielprogramm wird nun mit dem range-Befehl eine Schleife angelegt, welche die einzelnen Datenelemente berechnet. Diese Schleife wird genau 201-mal nacheinander durchlaufen und interpretiert. In einer weiteren Schleife werden einige Datenwerte ausgegeben. Da Python eine Skriptsprache ist, werden die Aktionen in den Schleifen nicht besonders schnell sein, da sie immer wieder neu interpretiert werden.Darum möchte ich Ihnen hier noch eine zweite Variante vorstellen, mit der die Berechnung deutlich schneller abläuft:
import numpy as np 

# Berechnung mit der Array-Syntax 
t = np.arange(0.0, 2.0, 0.01) 
s = 1.0 + np.sin(2 * np.pi * t) 

# Ausgabe der berechneten Daten (teilweise) 
for k in range(10, 20): 
    print(t[k], s[k]) 
Wie Sie im Beispielprogramm sehen können, ist die erste Rechenschleife komplett verschwunden. In der ersten Zeile wird das Array t mit Daten gefüllt. Hierzu wird die Funktion arange aus der numpy-Bibliothek benutzt. Der erste Parameter im arange-Aufruf ist der Startwert, es folgen der Endwert und die Schrittweite für die Befüllung der t-Arrays.Und nun kommt der Supertrick in der nächsten Zeile. Bei der Berechnung der Werte im Array s wird eine spezielle Funktionalität aus numpy benutzt (np.sin, spezielle Multiplikation und Addition), welche die Rechenoperationen wesentlich schneller ausführt.In [2] werden in der dort zu findenden Tabelle 1 die erforderlichen Rechenzeiten angegeben. Variante zwei ist etwa 15-mal so schnell wie Variante eins. Auch C#- oder C++-Programme sind nur noch unwesentlich performanter. Wenn es also möglich ist, sollte man die Rechenoperationen in Python immer mit der Funktionalität aus der numpy-Bibliothek ausführen.

Die Lösung der Übungsaufgabe

In der vorangegangenen Folge dieser Artikelserie [1] sollte ein Programm erstellt werden, welches bei ineinandergeschachtelten Ganzzahlen-Listen die Summen über die Ganzzahlwerte aller Listen berechnet. Hierzu muss über Listen in Listen in Listen ... iteriert werden. Ich habe schon angedeutet, dass hier eine Rekursion zum Einsatz kommen kann. Eine mögliche Lösung könnte wie in Listing 2 aussehen.
Listing 2: Ineinandergeschachtelte Listen verarbeiten
# Summe mit verschachtelten Listen&lt;br/&gt;def summe(liste):&lt;br/&gt;    teil = 0&lt;br/&gt;    for a in liste:&lt;br/&gt;        # Ist a wieder eine Liste?&lt;br/&gt;        if type(a) is list:&lt;br/&gt;            # Ja, summe rekursiv aufrufen&lt;br/&gt;            teil += summe(a)&lt;br/&gt;        else:&lt;br/&gt;            # Nein: Einfach addieren&lt;br/&gt;            teil += a&lt;br/&gt;&lt;br/&gt;    return teil&lt;br/&gt;&lt;br/&gt;# *** Hauptprogramm ***&lt;br/&gt;liste1 = [1, 5, 4, 8, 9]&lt;br/&gt;liste2 = [20, 30, 50, [-1, -2, -3]]&lt;br/&gt;liste = [liste1, 100, 200, liste2]&lt;br/&gt;print("Liste: ", liste)&lt;br/&gt;&lt;br/&gt;total = summe(liste)&lt;br/&gt;                                                                  &lt;br/&gt;print("Summe: ", total)&lt;br/&gt;# *** Ende Hauptprogramm ***  
Es wird eine Funktion summe erstellt, welche die Summe der Elemente einer Liste berechnen kann. Bei der Iteration über die Elemente der Liste wird jedoch jedes Mal geprüft, ob ein einfacher Wert oder wieder ein Listen-Objekt vorliegt (type(a) is list). In diesem Fall wird die summe-Funktion rekursiv für die innere Liste aufgerufen. Dieser Aufruf gibt dann natürlich die Summe der inneren Liste zurück und wird zur Hauptsumme addiert. Die Rekursionen können mehrfach ausgeführt werden, je nach Struktur der Liste.In der main-Methode wird eine komplizierte Liste erstellt, die summe-Funktion aufgerufen und anschließend das Ergebnis ausgegeben.

Und nun die nächste Aufgabe

Es soll noch ein bisschen mit Tupeln geübt werden, besonders als Rückgabe aus Funktionen.Schreiben Sie ein kleines Programm, welches eine Funk­tion enthält, die sowohl das Volumen als auch die Oberfläche einer Dose berechnet. Als Eingabeparameter übergeben Sie nur den Radius, als Rückgabewerte im return-Statement sollen die drei Werte für den Durchmesser, die Oberfläche und das Volumen zurückgegeben werden.Rufen Sie die Funktion aus der main-Methode auf, ordnen Sie die Rückgabewerte des Funktionsaufrufs den drei Variablen D, O und V zu und geben Sie die Ergebnisse im Konsolenfenster aus.

Zusammenfassung

In vierten Teil des Kurses haben wir weitere Container-Objekte kennengelernt. Außerdem haben wir fertige Bibliotheken angewendet und dabei eine einfache Möglichkeit für die einfache und schnelle Berechnung von vielen Datenwerten in Arrays kennengelernt.
Projektdateien herunterladen

Fussnoten

  1. Bernd Marquardt, Listenreich, Programmieren mit Python, Teil 3, dotnetpro 4/2020, Seite 128 ff., http://www.dotnetpro.de/A2004Python
  2. Bernd Marquardt, Arrays mit Schleife, Mathematik mit Python, Teil 1, dotnetpro 12/2017, Seite 64 ff., http://www.dotnetpro.de/A1712Rechnen
  3. Bernd Marquardt, Der Mathe-Turbo, Mathematik mit Python, Teil 2, dotnetpro 1/2018, Seite 78 ff., http://www.dotnetpro.de/A1801Rechnen
  4. Bernd Marquardt, Python kann noch mehr, Mathematik mit Python, Teil 3, dotnetpro 7/2018, Seite 70 ff., http://www.dotnetpro.de/A1807Rechnen
  5. Bernd Marquardt, Vielfältige Berechnungen, Mathe­matik mit Python, Teil 4, dotnetpro 8/2018, Seite 88 ff., http://www.dotnetpro.de/A1808Rechnen

Neueste Beiträge

DWX hakt nach: Wie gestaltet man intuitive User Experiences?
DWX hakt nach: Wie gestaltet man intuitive User Experiences? Intuitive Bedienbarkeit klingt gut – doch wie gelingt sie in der Praxis? UX-Expertin Vicky Pirker verrät auf der Developer Week, worauf es wirklich ankommt. Hier gibt sie vorab einen Einblick in ihre Session.
4 Minuten
27. Jun 2025
„Sieh die KI als Juniorentwickler“
CTO Christian Weyer fühlt sich jung wie schon lange nicht mehr. Woran das liegt und warum er keine Angst um seinen Job hat, erzählt er im dotnetpro-Interview.
15 Minuten
27. Jun 2025
IoT neu eingebunden - Integration und Verwaltung von IoT-Geräten mit Azure IoT Operations
Wie sich das neue Azure IoT Operations von bestehenden Azure-Diensten unterscheidet, welche Technologien dabei zum Einsatz kommen und wann sich der Umstieg lohnt.
16 Minuten
15. Jun 2025
Miscellaneous

Das könnte Dich auch interessieren

UIs für Linux - Bedienoberflächen entwickeln mithilfe von C#, .NET und Avalonia
Es gibt viele UI-Frameworks für .NET, doch nur sehr wenige davon unterstützen Linux. Avalonia schafft als etabliertes Open-Source-Projekt Abhilfe.
16 Minuten
16. Jun 2025
Mythos Motivation - Teamentwicklung
Entwickler bringen Arbeitsfreude und Engagement meist schon von Haus aus mit. Diesen inneren Antrieb zu erhalten sollte für Führungskräfte im Fokus stehen.
13 Minuten
19. Jan 2017
Bausteine guter Architektur - Entwurf und Entwicklung wartbarer Softwaresysteme, Teil 2
Code sauberer gestalten anhand von wenigen Patterns und Grundhaltungen.
6 Minuten
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige