Datentypen und Variablen
Einführung
Haben Sie bereits in Sprachen wie C und C++ programmiert? Deswegen glauben Sie, dass Sie bereits genug über Datentypen und Variablen wissen? Sie wissen sicherlich viel, das stimmt, aber nicht genug, wenn es um Python geht. Deshalb lohnt es sich auf jeden Fall hier weiter zu lesen.Es gibt haarige Unterschiede in der Art wie Python und C Variablen behandeln. Vertraute Datentypen wie Ganzzahlen (Integer), Fließkommazahlen (floating point numbers) und Strings sind zwar in Python vorhanden, aber auch hier gibt es wesentliche Unterschiede zu C und C++.
Möchte man Listen oder assoziative Arrays in C oder C++ verwenden, dann muss man sie sich selbst mühsam von Grund auf inklusive Zugriffs- und Suchfunktionen programmieren oder schwer verständlichen Libraries verwenden.
Dieses Kapitel ist also sowohl für Anfänger als auch für Fortgeschrittene Programmierer zu empfehlen.
Variablen
Eine Variable im allgemeinsten Sinne ist einfach ein Behälter (Container) zur
Aufbewahrung von bestimmten Werten, also z.B. Strings oder Zahlen. Wie der Name
sagt, sind Variablen "variabel", d.h. sie können ihren Wert ändern. Man kann im
Verlauf des Programms auf diese Variablen, oder genauer auf den Wert ihres Inhaltes
zugreifen, oder ihnen einen neuen Wert zuweisen.
In den meisten Programmiersprachen, wie z.B. C, ist es so, dass eine
Variable einen festen Speicherplatz bezeichnet, in dem Werte eines bestimmten
Datentyps abgelegt werden können. Während des Programmlaufes kann sich der
Wert der Variable ändern, aber die Wertänderungen müssen vom gleichen Typ
sein. Also man kann nicht in einer Variable zu einem bestimmten Zeitpunkt eine
Integerzahl gespeichert haben und dann diesen Wert durch eine Fließkommazahl
überschreiben. Ebenso ist der Speicherort der Variablen während des gesamten
Laufes konstant, kann also nicht mehr geändert werden. In Sprachen wie C wird
der Speicherort bereit durch den Compiler fixiert.
In Python sieht dies anders aus. Zunächst einmal bezeichnen Variablen in
Python keinen bestimmten Typ und deshalb benötigt man auch in Python keine
Typdeklaration. Benötigt man im Programm bespielsweise eine Variable i mit dem
Wert 42, so erreicht man dies einfach mit der folgenden Anweisung:
i = 42Obige Anweisung darf man nicht als mathematisches Gleichheitszeichen sehen, sondern als "der Variablen i wird der Wert 42 zugewiesen", d.h. der Inhalt von i ist nach der Zuweisung 42. Man kann diesen Wert der Variablen auch, wie man im folgenden Beispiel sieht, anschließend ändern:
>>> i = 42 >>> i = i + 1 >>> print(i) 43 >>>
Verändern von Datentypen und Speicherplätzen
Programmieren bedeutet Verarbeitung von Daten. Daten werden in einem Python-Programm durch Objekte repräsentiert. Diese Objekte können in Python- integriert (built-in) sein, d.h. sie gehören zum Standardsprachumfang, oder
- sie kommen aus Erweiterungsmodulen oder
- oder sie werden in der Anwendung des Benutzers erzeugt.
Zahlen
Python kennt vier eingebaute (built-in) Datentypen für Zahlen:
- Ganzzahl (Integer)
z.B. 4321
vorangestellte 0 bedeutet Oktalzahl und
vorangestelltes 0x bedeutet Hexzahl
- lange Ganzzahl
Sie können beliebig lang werden
Sie werden mit einem l am Anfang bzw. L am Ende bezeichnet.
- Fließkommazahlen
Zahlen der Form 3.14159 oder 17.3e+02
- komplexe Zahlen
z.B. 1.2+3j
Typwechsel bei Variablen
In Python kann eine Variable, wie bereits gesagt, sofort ohne Deklaration des Datentyps verwendet werden. Dennoch vergibt Python einen Datentyp, d.h. je nach Datentyp, wird die Variable anders angelegt, also als Integer, Float, String, und so weiter.Der Datentyp ist in Python nicht an die Variable, sondern an den Wert gebunden, was impliziert, dass sich der Typ zur Laufzeit ändern kann, wie wir im folgenden Beispiel sehen können:
i = 42 # Datentyp ist integer (implizit) i = 42 + 0.11 # Typ ändert sich zu float i = "fourty" # und jetzt ein String
Wechselnde Speicherorte
Prinzipiell wird sich vorigen Fall, wobei das natürlich implementierungsabhängig ist, der Speicherort für die Variable i ändern. Der Interpretor kann bei der Anweisung "i = 42" den Wert als Integer abspeichern, muss aber bei der Anweisung "i = 42 + 0.11" einen neuen Ort für eine Float-Zahl anlegen. Für i = "fourty" muss er in einen String gewandelt werden.Achtung: Als Anwender braucht man dies eigentlich nicht zu wissen, da ja alles automatisch geschieht!
Betrachten wir nun folgenden Python-Code:
>>> x = 3 >>> y = x >>> y = 2
Intuitiv würde man davon ausgehen, dass Python zunächst für x einen Speicherort wählt und dort das Objekt (Zahl) 3 abspeichert. Anschließend wird der Variablen y der Wert von x zugewiesen. In C und vielen anderen Programmiersprachen würde auch für y ein eigener Speicherort bestehen, in dem nun die Zahl 3 hineingeschrieben würde. Python geht anders vor: x ist eine Variable mit dem Objekt 3 und y ist eine Variable mit dem "selben" (nicht "gleichen") Objekt. x und y "zeigen" auf das gleiche Objekt. In der letzten Zeile wird y nun der Wert 2 zugewiesen, jetzt muss ein neues Objekt angelegt werden und y "zeigt" auf einen neuen Speicherort. (Anm: Dieses eben verwendete "zeigen" sollte von C-Programmierern keinesfalls mit den unter C verwendeten Pointern verwechselt werden.)
Es stellt sich nun die Frage, wie man das oben gesagte überprüfen kann. Dazu bietet sich die Identitätsfuntion id() an. Die Identität einer Instanz dient dazu, sie von allen anderen Instanzen zu unterscheiden. Die Identität ist eine Ganzzahl, und sie ist innerhalb eines Programmes eindeutig. Die Identitätsfunktion id() liefert die Identität. So kann man prüfen, ob es sich um eine bestimmte Instanz handelt und nicht nur um eine mit dem gleichen Wert und Typ. Wir geben nochmals das obige Beispiel ein, lassen uns aber jeweils die Identität ausgeben:
>>> x = 3 >>> print(id(x)) 157379912 >>> y = x >>> print(id(y)) 157379912 >>> y = 2 >>> print(id(y)) 157379924 >>> print(id(x)) 157379912 >>>Wir stellen fest, dass sich die Identität erst ändert, nachdem wir y einen neuen Wert zugewiesen haben. Die Identität von x bleibt gleich, d.h. der Speicherort von x wird nicht geändert.
Besonderheiten bei Strings
Einen besonderen Effekt können wir bei Strings feststellen. Im folgenden Beispiel wollen wir dies veranschaulichen. Dazu benötigen wir noch den "is"-Operator. Sind a und b zwei Strings, dann prüft "a is b", ob a und b die gleiche Identität (Speicherort) haben. Wenn "a is b" gilt, dann gilt auch "a == b". Aber wenn "a == b" gilt, dann gilt natürlich nicht notwendigerweise auch "a is b"!Nun wollen wir untersuchen, wie Strings in Python abgespeichert werden. Im folgenden Beispiel, erkennen wir, dass a und b sich den gleichen Speicherort teilen, obwohl wir diesmal keine Zuweisung der Art "b = a" verwendet haben:
>>> a = "Linux" >>> b = "Linux" >>> a is b TrueWie sieht es jedoch aus, wenn der verwendete String länger ist? Im folgenden verwenden wir als String den längsten Ortsnamen der Welt. Eine Gemeinde mit etwas mehr als 3000 Einwohnern im Süden der Insel Anglesey in der gleichnamigen Grafschaft im Nordwesten von Wales:
>>> a = "Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch" >>> b = "Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch" >>> a is b TrueAber Vorsicht ist geboten, denn was für eine Gemeinde in Wales funktioniert, schlägt beispielsweise für Baden-Württemberg fehl:
>>> a = "Baden-Württemberg" >>> b = "Baden-Württemberg" >>> a is b False >>> a == b TrueAlso an der geographischen Lage kann es nicht liegen, wie man im folgenden Beispiel sieht. Es sieht vielmehr so aus, als dürften keine Sonderzeichen oder Blanks im String vorkommen.
>>> a = "Baden!" >>> b = "Baden!" >>> a is b False >>> a = "Baden1" >>> b = "Baden1" >>> a is b True
String-Interna
Die Aufgabe der Computer der ersten Generation in den Vierziger- und Fünfzigerjahren des vorigen Jahrhunderts
bestand - wegen technischer Restriktionen - im Wesentlichen in der Lösung numerischer Probleme. Textverarbeitung
war zu dieser Zeit nur ein Traum. Heutzutage beschäftigen sich Computer in einem großen Umfang mit Textverarbeitung;
die bedeutendste Anwendungen stellen wohl die Suchmaschinen, allen voran Google, dar. Um mit Texten umzugehen,
benötigen Programmiersprachen geeignete Datentypen. So werden Strings in nahezu allen modernen Programmiersprachen
genutzt um textuelle Informationen zu verarbeiten. Logisch gesehen ist eine String - wie Text - eine Folge von
Zeichen. Die Frage bleibt, was ein Zeichen (englisch character) darstellt. In einem Buch oder auch einem Text,
wie den, den Sie gerade lesen, werden die Zeichen grafisch dargestellt, sogenannte Grapheme, die aus Linien und
Kurven bestehen, die in bestimmten Verhältnissen angeordnet sind, sich überschneiden und so weiter.
In der Computertechnik stellt ein Character eine Einformationseinheit dar. Die Zeichen (characters) entsprechen
Graphemen, den zugrundeliegenden Einheiten der geschriebenen und gedruckten Sprache. Vor Unicode gab es nur eine
Eins-zu-eins-Beziehung zwischen Zeichen und Bytes, d.h., jedes einzelne Zeichen wurde durch ein Byte im Computer
repräsentiert. Ein solches Byte repräsentiert dann das logische Konzept des Zeichens und steht für die
gesamte Klasse der Grapheme dieses Zeichens. In dem Bild auf der rechten Seite zeigen wir verschiedene grafische
Repräsentationen des Buchstaben "A", also "A" in verschiedenen Fonts. Wir sehen, dass es im Druck verschiedene
grafische Darstellungen des abstrakten Konzeptes "A" gibt. (Übrigens geht unser heutiges A auf eine ägyptische
Hieroglyphe zurück, die einen Ochsenkopf symbolisiert)
Alle grafischen Darstellungen haben bestimmte Eigenschaften gemeinsam. Ein großes "A" wird in ASCII mit dem
Byte-Wert 65 kodiert.
ASCII ist allerdings auf 256 Bytes oder Zeichen beschränkt. Das ist natürlich genug für Sprachen wie Englisch,
Deutsch oder Französisch, aber bei weitem nicht genug für Schriftsysteme, wie sie das Chinesische oder das
Japanische erfordern. Wegen dieser Probleme wurde Unicode ins Leben gerufen. Unicode ist ein Standard, der
entworfen worden ist, um jedes beliebige Zeichen von jeder Schrift darstellen zu können. Verschiedene Schriftsysteme
können damit auch gleichzeitig verwendet werden, so kann ein Text in lateinischer Schrift beispielsweise mit
kyrillischem, arabischen oder sogar chinesischem Text gemischt werden. Jeder Buchstabe bzw. Zeichen wird in Unicode
als eine 4-Byte große Zahl dargestellt, so entspricht beispielsweise das große "A" dem Wert U+0041.
Während ASCII auf 256 pro Zeichensatz beschränkt ist, gibt es bei Unicode praktisch gesehen keinerlei Einschränkungen,
denn mit 4 Bytes kann man 2564 (also mehr als 429 Millionen) abbilden. (Wegen der gewählten Zuordnung
gibt es allerdings "nur" 1.112.064 mögliche Zeichen)
Nur wenig mehr als 100.000 Zeichen also weniger als 1% des theoretisch möglichen Zeichenraumes sind
in Unicode belegt. Aber die Frage bleibt, wie man Unicode implementiert. Wir könnten natürlich 4 Bytes pro Zeichen
verwenden und damit den Standard Ein-zu-eins umsetzen. Aber das wäre eine Speichervergeudung. Ein Textdokument
würde dann viermal so viele Speicherplatz benötigen wie bisher unter ASCII.
Es gibt verschiedene Zeichncodierungen für Unicode:
|
|
|---|---|
| Name | Beschreibung |
| UTF-32 |
Hierbei handelt es sich um eine Eins-zu-eins Abbildung, d.h. jedes Unicode-Zeichen, was ja einer 4-Byte großen
Zahl entspricht, wird auch in 4 Bytes abgespeichert.
Ein Vorteil dieser Kodierung besteht darin, dass das n-te Zeichen in einem String in linearer Zeit berechnet
(gefunden) werden kann, weil das n-te Zeichen immer an der Position 4×N vom Stringanfang aus gesehen beginnt.
Der hohe Speicherverbrauch dieser Kodierung ist allerdings ein Problem.
|
| UTF-16 |
Kaum jemand benötigt mehr als 65535, daher ist UTF-16, welches nur 2 Bytes benötigt, eine effizientere Alternative
zu UTF-32.
Ein Problem, dass sowohl UTF-32 und UTF-16 betrifft besteht darin, dass die Byte-Reihenfolge eines Zeichens vom Betriebssystem abhängt. |
| UTF-8 |
UTF8 ist eine Kodierung für Unicode mit variabler Länge, d.h. verschiede Zeichen
können verschiedene Kodierungslängen haben. So benötigen ASCII-Zeichen nur ein Byte pro Zeichen, die sogar dem
original ASCII-Zeichensatz für die ersten 128 Zeichen entsprechen. Das bedeutet, dass diese Zeichen zwischen UTF-8
und ASCII ununterscheidbar sind. Aber die sogenannten "Erweiterten Lateinischen" Zeichen, denen beispielsweise
die Umlaute wie ä, ö, ü angehören, benötigen zwei Bytes. Chinesische Zeichen benötigen drei und nur ganz
spezielle extrem selten benutzte Spezialzeichen benötigen 4 Bytes in UTF-8.
Ein Nachteil dieser Methode besteht darin, das n-te Zeichen eines Strings zu bestimmen. Die Berechnungszeit ist dann nicht mehr linear. |
Benutzung von Strings
Ein String, oder Zeichenkette, kann man als eine Sequenz von einzelnen Zeichen sehen.
Jedes einzelne Zeichen eines Strings, kann über einen Index angesprochen werden.
Im folgenden Beispiel sehen wir, wie der obige im Bild dargestellt String in Python
definiert wird und wie wir auf ihn zugreifen können:
>>> s = "Python" >>> print(s[0]) P >>> print(s[3]) hDie Länge eines Strings kann man mit der Funktion len() bestimmen und damit kann man auch einfach beispielsweise auf das letzte oder vorletzte Zeichen eines Strings zugreifen:
>>> s = "Python" >>> index_last_char = len(s) - 1 >>> print(s[index_last_char]) n >>> print(s[index_last_char - 1]) o >>>Da es bei der praktischen Arbeit sehr häufig vorkommt, dass man auf einzelne Zeichen eines Strings von hinten zugreifen muss, wäre es sehr lästig, wenn man dies immer über den Umweg durch den Aufruf der Funktion len() machen müsste. Python bietet deshalb eine elegantere Möglichkeiten. Die Indices werden auch von rechts durch negative Indices nummeriert, d.h. das letzte Zeichen wird mittels des Index -1, das vorletzte mittels -2 usw. angesprochen. Wir sehen dies in der folgenden Abbildung veranschaulicht:
Im Code sieht das wie folgt aus:
>>> s = "Python" >>> last_character = s[-1] >>> print(last_character) n
Strings können unter Benutzung von
- einzelnen Anführungszeichen (')
'Dies ist ein String mit einfachen Quotes' - doppelten Anführungszeichen (")
"Mayers' Dackel heißt Waldi" - dreifachen Anführungszeichen (''') oder (""")
'''String in dreifachen Anführungszeichen können auch über mehrere Zeilen gehen und 'einfache' und "doppelte" Anf.zeichen enthalten.'''
Wichtige Stringfunktionen
Ein paar String-Funktionen:- Konkatenation (englisch: Concatenation)
Diese Funktion dient dazu mittels des "+"-Operators zwei Strings zu einem neuein String zusammenzuhängen:
"Hello" + "World" -> "HelloWorld" - Wiederholung (englisch: Repetition)
Ein STring kann wiederholt konkateniert werden. Dazu benutzt man den "*"-Operator.
Beispiel:
"*-*" * 3 wird zu "*-**-**-*" - Indexing
"Python"[0] -> "P" - Slicing
Das englische Verb "to slice" bedeutet in Deutsch "schneiden" oder auch "in Scheiben schneiden". Letztere Bedeutung entspricht auch der Funktion von Slicing in Python. Man schneidet sich gewissermaßen eine "Scheibe" aus einem String heraus. [2:4] bedeutet im folgenden Ausdruck, dass wir aus dem String "Python" einen Teilstring herausschneiden, der mit dem Zeichen des Index 2 (inklusive) beginnt und bis zum Index 4 (exklusive) geht:
"Python"[2:4] -> "th"
- Länge eines Strings
len("Python") -> 6
Unveränderliche Zeichenketten
Wie in Java aber nicht wie in C oder C++, können String in Python nicht verändert werden. Versucht man eine indizierte Position zu ändern, erzeugt man eine Fehlermeldung:>>> s = "Some things are immutable!" >>> s[-1] = "." Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'str' object does not support item assignment >>>
Escape- oder Fluchtzeichen
Es gibt Zeichenfolgen, die den Textfluss steuern, wie zum Beispiel ein Newline (Zeilenvorschub) oder Tabulator. Sie lassen sich nicht auf dem Bildschirm als einzelne Zeichen darstellen. Die Darstellung solcher Zeichen innerhalb von String-Literalen erfolgt mittels spezieller Zeichenfolgen, sogenannter Escape-Sequenzen. Eine Escape-Sequenz wird von einem Backslash \ eingeleitet, gefolgt von der Kennung des gewünschten Sonderzeichens. Übersicht der Escape-Zeichen:- \ Zeilenfortsetzung
- \\ Rückwärtsschrägstrich
- \' Einzelnes Anführungszeichen
- \" Doppeltes Anführungszeichen
- \a Glocke
- \b Rückschritt
- \e Ausmaskieren
- \0 Null
- \n Zeilenvorschub (linefeed, LF)
- \v Vertikaler Tabulator
- \t Horizontaler Tabulator
- \r Wagenrücklauf (carriage return, CR)
- \f Seitenvorschub
- \0XX Oktaler Wert
- \xXX Hexadezimaler Wert
Beispiel:
r"\n bewirkt einen Zeilenvorschub"
Byte-Strings
Python 3 benutzt die Konzepte Text und (binäre) Daten, statt Strings und 8-bit Strings wie in Python 2.x. Jeder String oder Text in Python 3 ist Unicode, aber kodiertes Unicode wird als Datenstrings in Bytes dargestellt. Der Datentyp für Text ist str; der Datentyp für Daten ist bytes. Es ist nicht möglich Text und Daten (bytes) zu mischen. Versucht man zu mischen, erhält man den Ausnahmefehler TypeError.Während ein String-Objekt eine Folge von Zeichen enthält, enthält ein byte-Objekt eine Folge von Bytes mit den Werten 0 .. 255, die den ASCII-Werten entsprechen. Während ein String-Objekt eine Folge von Zeichen in Unicode enthält, enthält ein Byte-Objekt eine Folge von Bytes, aus dem Bereich 0 bis 255, die die ASCII-Werte repräsentieren.
Zur Verdeutlichung ein Beispiel:
>>> x = b"Hallo"
>>> t = str(x)
>>> u = t.encode("UTF-8")
In Python 3.x kann man nicht mehr explizit einen Unicode-String durch Voranstellung eines "u" definieren.
Dies ist natürlich auch nicht notwendig, da String ohnehin automatisch Unicode sind:
>>> txt = u"Geht nicht"
File "<stdin>", line 1
txt = u"Geht nicht"
^
SyntaxError: invalid syntax
>>>

