2 Datentypen und Datenstrukturen
In diesem Kapitel werden die einfachen Datentypen String, Integer und Float beschrieben. Außerdem lernen Sie, welche Operationen Sie auf den Datenstrukturen Tupel, Set, Liste und Dictionary ausführen können.
Daten sind Zeichen, die eine Information enthalten. Sie werden aus Beobachtungen oder Messungen gewonnen.
Nun zwingt uns die Komplexität der Realität allerdings dazu, selektiv vorzugehen: Aus der Fülle der Daten wählen wir nur diejenigen aus, die zur Lösung eines Problems beitragen. Nach der Auswahl müssen die Daten noch geordnet werden, sie müssen also in eine Struktur gebracht werden. Sollen z. B. Informationen über lieferbare Bücher in einer Datenbank erfasst werden, dann sind der Name des Autors, der Buchtitel, das Erscheinungsjahr und der Preis relevante Daten. Diese Daten werden typischerweise in Form von Tabellen in Datenbanken gespeichert (siehe Tabelle 2.1).
Nr. |
Autor |
Titel |
Jahr |
Preis |
---|---|---|---|---|
1 |
Kofler |
Python. Der Grundkurs |
2020 |
14,90 |
2 |
Wirth |
Algorithmen und Datenstrukturen |
1999 |
37,99 |
Tabelle 2.1 Strukturierung von Daten
Wie die Daten strukturiert werden sollten, richtet sich nach ihrem Verwendungszweck. Wenn die Daten der realen Welt mit einem Computer verarbeitet werden sollen, müssen sie so modelliert werden, dass dies einfach möglich ist, denn ein Rechner kann nur mit bestimmten Strukturen effektiv arbeiten.
In der praktischen Informatik ist eine Datenstruktur eine Menge von Objekten, die nur mit genau festgelegten Operationen manipuliert werden dürfen. Auf eine Kurzformel gebracht: Datenstruktur = Objekte + Operationen. Die Daten werden in einer für die jeweilige Datenstruktur optimalen Art und Weise organisiert, um auf sie so effizient wie möglich zugreifen zu können.
Datenstrukturen kommen nicht isoliert vor, sondern sie sind immer in Computerprogramme integriert und mit Algorithmen verknüpft. Niklas Wirth, ein großer Pionier der Informatik, beschreibt sie sogar als Basis der Programmierung: »Programme sind letztlich konkrete Formulierungen abstrakter Algorithmen, die sich auf bestimmte Darstellungen und Datenstrukturen stützen.«
Python stellt die eingebauten Datenstrukturen Tupel, Set, Liste und Dictionary zur Verfügung. Bis auf Sets werden diese Datenstrukturen in den folgenden Kapiteln intensiv genutzt. Ich möchte sie Ihnen daher in den nächsten Abschnitten vorstellen.
2.1 Tupel
Ein Tupel ist eine Sequenz von Elementen, die iterierbar sind, aber nicht verändert werden können. Die Elemente eines Tupels müssen nicht vom gleichen Typ sein. Die Unveränderbarkeit ist das entscheidende Merkmal eines Tupels.
Definiert wird ein Tupel, indem die Elemente durch Kommata getrennt in runde Klammern eingeschlossen werden.
Mit den eingebauten Python-Funktionen type() und id() können Sie den Typ und die Identität (Speicheradresse) eines Tupels ermitteln (siehe Abschnitt 3.2 ). Auf die einzelnen Elemente eines Tupels kann lesend mit dem []-Operator zugegriffen werden.
Der folgende Konsolendialog demonstriert einen ersten Zugang:
>>> B=('Wirth','Datenstrukturen',1999,37.99)
>>> B[0]
'Wirth'
>>> B[1]
'Datenstrukturen'
>>> B[2]
1999
>>> type(B)
<class 'tuple'>
>>> type(B[0])
<class 'str'> #Zeichenkette
>>> type(B[2])
<class 'int'> #Ganzzahl
>>> type(B[3])
<class 'float'> #Gleitpunktzahl
>>> id(B)
140560994071888
>>> id(B[0])
140560994033328
In der ersten Zeile wird ein Tupel erzeugt, indem die vier Elemente, durch Kommata getrennt, in runde Klammern eingeschlossen werden. Die runden Klammern sind nicht zwingend notwendig. Sie können sie auch weglassen. Durch die Zuweisung mit dem Zuweisungsoperator = werden die Daten des Tupels in dem Objekt B gespeichert. Mit B[0] wird das erste Element des Tupels ausgelesen. Als Typ von B wird <class 'tuple'> ermittelt. Das bedeutet, dass B ein Objekt der Klasse tuple ist. B[0] ist ein Objekt der Klasse str (String: Zeichenkette), B[2] ist ein Objekt der Klasse int (Ganzzahl), B[3] ist ein Objekt der Klasse float.
Bei den mit der Funktion id() ermittelten Zahlen handelt es sich um die Identitäten der Objekte, die man sich als Speicheradressen im Arbeitsspeicher (RAM) vorstellen kann. Jedes Element des Tupels hat eine eigene Identität, also eine eigene Adresse im Arbeitsspeicher des Rechners.
Diese Analyse bringt die folgende zentrale Erkenntnis: In Python ist alles ein Objekt, unabhängig davon, ob es sich um eine »einfache« Ganzzahl oder um eine lange Zeichenkette (String) oder eine komplizierte Datenstruktur handelt. Jedes Objekt ist immer ein Exemplar einer Klasse class. Das interne Design der Programmiersprache Python orientiert sich durchgehend am Paradigma objektorientierter Sprachen: Objekte sind die grundlegende Abstraktion von Daten, und alle Daten werden von Python als Objekte bzw. als Relationen zwischen Objekten dargestellt. Jedes Objekt hat einen Wert, eine Identität und einen Typ. (Ausführliche Informationen dazu finden Sie in der Python-Dokumentation unter https://docs.python.org/3/reference/datamodel.html.)
Objekt
Ein Objekt hat einen bestimmten Wert, eine Identität und einen Typ. Der Typ gibt an, zu welcher Klasse ein Objekt gehört.
Abbildung 2.1 veranschaulicht diesen Zusammenhang.
Abbildung 2.1 Bestandteile eines Objekts
2.1.1 Exkurs: Elementare Datentypen von Python
Die mit der eingebauten Funktion type() durchgeführte Analyse des Tupels B hat ergeben, dass sich das Objekt B aus drei elementaren Datentypen zusammensetzt. Das sind:
-
String (Zeichenkette)
-
Integer (Ganzzahlen)
-
Float (Gleitpunktzahlen)
String
Ein String (Typ <class str>) ist eine Zeichenkette aus einer Folge von Ziffern oder Buchstaben, die in einfache oder doppelte Anführungszeichen '...' oder "..." eingeschlossen werden müssen. Zeichenketten können nicht verändert werden. Man sagt, sie sind unveränderbar (engl. immutable). Das heißt: In einem String-Objekt können Sie keine Zeichen einfügen oder entfernen. Der folgende Konsolendialog zeigt, wie Sie zwei Strings verketten und dessen Länge ermitteln können:
>>> A="Auto"
>>> B="fahren"
>>> C=A+B
>>> C
'Autofahren'
>>> C[5]
'a'
>>> len(C)
10
Ganzzahl
Ganzzahlen (Typ <class int>) repräsentieren die aus der Mathematik bekannten natürlichen Zahlen. Deren Wertebereich wird nur durch die Speicherkapazität Ihres Computers begrenzt. Wenn Sie z. B. in Ihrer Python-Konsole 2128 eingeben:
>>> 2**128
erhalten Sie folgende Ausgabe:
340282366920938463463374607431768211456
Den Exponenten können Sie so lange erhöhen, bis die Speicherkapazität Ihres Computers erschöpft ist.
Gleitpunktzahl
Gleitpunktzahlen (engl. floating point numbers) vom Typ <class float> sind Zahlen, die in der Sprache der Mathematik als Dezimalbrüche bezeichnet werden. Eine Gleitpunktzahl ist eine angenäherte Darstellung einer reellen Zahl. Die sonst noch gebräuchlichen Bezeichnungen »Gleitkommazahl« oder »Fließkommazahl« werden hier nicht verwendet, weil in der praktischen Informatik als Dezimaltrennzeichen ein Punkt und nicht ein Komma verwendet wird.
Gleitpunktzahlen werden im 64-Bit-Format dargestellt. Die größte darstellbare Zahl beträgt etwa 1,8 × 10308. Wenn Sie in die Python-Konsole 10308 eingeben:
>>> 1e308
erhalten Sie als Ausgabe:
1e+308
Dieser Wert wird also noch akzeptiert. Wenn Sie dagegen
>>> 1.8e308
eingeben, dann erhalten Sie
inf
als Ausgabe. inf ist eine interne Konstante von Python. Sie steht für unendlich.
Die kleinste darstellbare Zahl, die größer als 0 ist, hat einen Wert von etwa 5 × 10–324. Alle Zahlen zwischen 0 und dieser Zahl werden als 0.0 interpretiert:
Diese Ober- und Untergrenzen werden Sie in den meisten praktischen Anwendungen selten erreichen. Da in der Mathematik aber häufig numerische Näherungslösungen als Grenzwerte gebildet werden, sollten Sie diese Einschränkung im Hinterkopf behalten.
Der Vergleich von Tupeln
In dem nächsten Konsolendialog werden drei Tupel miteinander verglichen:
>>> T1=(5,7,9)
>>> T2=(9,7,5)
>>> T3= 5,7,9
>>> T1==T3
True
>>> T1==T2
False
>>> T1<T2
True
>>> T1>T2
False
In den Zeilen 1 und 2 werden zwei Tupel T1 und T2 mit gleichen Einträgen erzeugt. Die Einträge unterscheiden sich jedoch in der Reihenfolge. Das Tupel T3 in Zeile 3 hat die gleichen Elemente wie T1. Der Vergleich von T1 und T3 zeigt, dass diese beiden Tupel gleich sind. Die runden Klammen können Sie auch weglassen (siehe: Tupel T3). Der Vergleich von T1 und T2 zeigt, dass diese beiden Tupel nicht gleich sind. Offenbar kommt es auf die Reihenfolge der Einträge an. Wenn sich die Reihenfolge der Einträge ändert, sind die Tupel nicht mehr gleich. Tupel sind nur dann gleich, wenn sie die gleichen Elemente haben und in der gleichen Reihenfolge angeordnet sind.
Das Tupel T1 ist kleiner als das Tupel T2. Zwei Tupel werden, beginnend mit dem ersten Element, elementweise miteinander verglichen.
Operationen auf Tupeln
Tupel können addiert und multipliziert werden: Die Elemente werden dann in ihrer ursprünglichen Reihenfolge aufgelistet. Gleiche Elemente können also mehrmals vorkommen:
>>> T1+T2
(5,7,9,9,7,5)
>>> 3*T1
(5, 7, 9, 5, 7, 9, 5, 7, 9)
>>> T1[0]=11
Traceback (most recent call last):
File "<pyshell>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
Der Versuch, ein neues Element in ein Tupel einzufügen, scheitert. Das muss so sein, denn Tupel sind unveränderbar (engl. immutable). Es können weder Werte verändert noch hinzugefügt werden.
2.1.2 Würfelsimulation
Listing 2.1 simuliert ein Würfelspiel mit drei Würfeln, das zeigen soll, dass ein Tupel auch links vom Zuweisungsoperator stehen darf.
01 #01_tupel1.py
02 from random import randint
03 (a,b,c)=randint(1,6),randint(1,6),randint(1,6)
04 print("Würfelsimulation")
05 print("Würfel a:",a)
06 print("Würfel b:",b)
07 print("Würfel c:",c)
Listing 2.1 Würfelsimulation mit einem Tupel
Ausgabe
Würfelsimulation
Würfel a: 5
Würfel b: 4
Würfel c: 1
Analyse
In Zeile 02 wird die Methode randint aus dem Modul random importiert. Eine Methode ist ein Unterprogramm, das innerhalb einer Python-Klasse definiert wird. Die Methode randint(a,b) erzeugt ganze Zufallszahlen in dem Intervall von a bis b.
In Zeile 03 erzeugt die Methode randint(1,6) drei Zufallszahlen zwischen 1 und 6. Diese Zahlen werden in das Tupel (a,b,c) gespeichert und in den Zeilen 05 bis 07 ausgegeben. Die runden Klammern sind optional.
2.1.3 Vertauschen von Objekten
In Sortieralgorithmen müssen zwei Zahlen miteinander getauscht werden, wenn der Nachfolger kleiner ist als sein Vorgänger. Wenn Sie in der Python-Konsole einem Tupel a,b zwei Werte zuweisen, dann können Sie die Reihenfolge des Wertepaars tauschen:
>>> a,b = 17,13
>>> a
17
>>> b
13
>>> a,b = b,a
>>> a
13
>>> b
17
Ein Tupel muss nicht unbedingt in Klammern eingeschlossen werden. In der ersten Zeile ist a=17 und b=13. In der sechsten Zeile wird a der Wert von b zugewiesen und b der Wert von a. Jetzt ist a=13 und b=17. Das dürfte aber eigentlich nicht funktionieren, denn Tupel sind unveränderbar. Listing 2.2 liefert die Erklärung:
01 #02_tuple2.py
02 a,b=17,13
03 T=(a,b)
04 print("----------vor dem Tausch------------")
05 print("a = %i | b = %i" %(a,b))
06 print("%i | %i" %(id(a),id(b)))
07 print("T =",T," id:",id(T))
08 a,b=b,a
09 print("----------nach dem Tausch-----------")
10 print("a = %i | b = %i" %(a,b))
11 print("%i | %i" %(id(a),id(b)))
12 print("T =",T," id:",id(T))
Listing 2.2 Elemente aus einem Tupel vertauschen
Ausgabe
----------vor dem Tausch------------
a = 17 | b = 13
4446407968 | 4446407840
T = (17, 13) id: 140371077495136
----------nach dem Tausch-----------
a = 13 | b = 17
4446407840 | 4446407968
T = (17, 13) id: 140371077495136
Analyse
In Zeile 02 werden dem Tupel a,b die Werte 17,13 zugewiesen. Die Werte von a und b werden in Zeile 03 dem Objekt T zugewiesen.
In den Zeilen 05 bis 07 werden die Werte der Variablen a und b sowie die Einträge des Tupels vor dem Tausch ausgegeben. In Zeile 07 wird auch noch die Identität des Tupels id(T) ausgegeben. In Zeile 08 findet der Tausch statt.
Die Ausgaben in den Zeilen 10 bis 12 zeigen, dass nur die Identitäten (Speicheradressen) von a und b getauscht werden. Die Identität des Tupels T hat sich nicht geändert.