Datentypen und Variablen



Einführung

Variablen als Container 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 weiterzulesen.

Es gibt gravierende Unterschiede in der Art wie Python und C bzw. 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++. Dieses Kapitel ist also sowohl für Anfänger als auch für fortgeschrittene Programmierer zu empfehlen.

Möchte man Listen oder assoziative Arrays in C oder C++ verwenden, dann muss man sie sich entweder selbst mühsam von Grund auf -- inklusive Zugriffs- und Suchfunktionen -- programmieren oder man muss schwer verständliche Libraries verwenden.

Variablen

Variablen im Allgemeinen

Variablen als leere Schuhkartons Dieses Unterkapitel ist speziell für C, C++ und Java-Programmierer gedacht, weil diese Programmiersprachen Standarddatentypen anders behandeln als Python. Diejenigen, die Python als erste Programmiersprache lernen, können gerne zum nächsten Unterkapitel springen.

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 kann man nicht in einer Variablen 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 und C++ wird der Speicherort bereits durch den Compiler fixiert.

Was wir bisher gesagt haben, trifft auf Sprachen wie C, C++ und Java zu. In diesen Sprachen müssen Variablen auch deklariert werden, bevor sie benutzt werden können.

int x;
int y;


Solche Deklarationen stellen sicher, dass das Programm Speicher für zwei Variablen mit den Namen x und y reserviert. Die Variablen Namen stehen für den Speicherort. Es ist so wie die beiden leeren Schuhkartons im Bild auf der rechten Seite. Diese beiden Schuhkartons sind mit x und y etikettiert. So wie die Schuhkartons ist auch der Speicher für x und y leer.

Will man Werte im Speicher ablegen, so kann man dies mit Zuweisungen verwirklichen. Die Art, wie man Werte zuweist, ist in den meisten Programmiersprachen gleich. Meistens wird das Gleichheitszeichen benutzt. Der Wert auf der rechten Seite wird in der Variablen auf der linken Seite gespeichert.
Wir werden im folgenden C-Beispiel an beide vorher deklarierte Variablen den Wert 42 zuweisen. Das Ergebnis sehen wir auch im Bild veranschaulicht.

x = 42;
y = 42;


Variablenzuweisung in C, C++ und Java, versinnbildlicht in Schuhkartons

Es ist nicht schwer zu verstehen, was passiert, wenn wir einen neuen Wert einer der beiden Variablen zuweisen, zum Beispiel den Wert 78 der Variablen y.

y = 78;

Der Wert der Speicherstelle von y wurde ausgetauscht.

Variablenzuweisungen in C oder Java

Wir haben nun gesehen, dass in Programmiersprachen wie C, C++ oder Java jede Variable einen eindeutigen Datentyp hat oder besser haben muss. Das bedeutet, dass falls beispielsweise eine Variable vom Typ Integer ist, sie nur Integer-Werte aufnehmen kann. In diesen Programmiersprachen müssen Variablen auch vor ihrer Benutzung deklariert werden. Deklaration bedeutet Bindung an einen Datentyp, der dann für den gesamten Programmablauf unveränderlich ist.



Variablen in Python



In Python haben wir eine gänzlich andere Situation. 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 = 42
Diese Anweisung bezeichnet man als Zuweisung oder auch als Definition einer Variablen. Das Gleichheitszeichen 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.

Wir können nun diese Variable zum Beispiel benutzen, indem wir ihren Wert mit print ausgeben lassen:

>>> print("Wert von i: ", i)
Wert von i:  42
>>> 

Wir können sie aber auch auf der rechten Seite einer Zuweisung in einem Rechenausdruck verwenden, dessen Ergebnis dann einer Variablen zugewiesen wird:

>>> x = i / 3.0 + 5.8
>>> print(x)
19.8
>>> 

Man kann eine Variable auch gleichzeitig auf der rechten und der linken Seite einer Zuweisung benutzen. Im Folgenden erhöhen wir den Wert der Variablen i um 1:

>>> i = i + 1
>>> print(i)
43
>>> 
Für obige Schreibweise wird üblicherweise die sogenannte erweiterte Zuweisung verwendet:
>>> i = 42
>>> i += 1
>>> i
43
>>> 

Vom Ergebnis her gesehen sind die beiden Möglichkeiten identisch. Allerdings gibt es einen Implementierungsunterschied. Bei der erweiterten Zuweisung muss die Referenz der Variablen i nur einmal ausgewertet werden. Bei Datentypen wie Listen, die wir noch nicht behandelt haben, kann es zu extremen Laufzeitproblemen kommen, wenn man keine erweiterte Zuweisung verwendet. Darauf gehen wir jedoch erst später ein.
Erweiterte Zuweisungen gibt es auch für die meisten anderen Operatoren, wie ,,-'', ,,*'', ,,/'', ,,**'', ,,//'' (ganzzahlige Division), ,,%'' (Modulo).

In Python kann zur Laufzeit sowohl der Wert einer Variablen geändert werden, als auch der Typ einer Variablen. Präziser: Ein neues Objekt eines beliebigen Typs wird der Variablen zugewiesen. Wir verdeutlichen dies in folgendem Beispiel:
i = 42		# Typ wird implizit auf Integer gesetzt
i = 41 + 0.11	# Typ wird in float geändert
i = "fünfzig"	# Jetzt ist es ein string


Objekt-Referenzen

Wir wollen einen genaueren Blick auf die Implementierung von Variablen in Python werfen. Variablen referenzieren Objekte in Python. Die eigentlichen Daten sind jedoch im Objekt enthalten.

Python Variable



Da Variablen Objekte referenzieren und Objekte einen beliebigen Datentyp haben können, ist es nicht möglich, Variablen mit Datentypen zu verknüpfen. Dies ist ein riesiger Unterschied zu C, C++ oder Java, wo Variablen fest mit einem Datentyp assoziiert sind. Diese Verknüpfung gilt solange das Programm läuft.

Aus diesem Grund ist es möglich in Python Code wie den folgenden zu schreiben:
>>> x = 42
>>> print(x)
42
>>> x = "Jetzt referenziert x einen String"
>>> print(x)
Jetzt referenziert x einen String
Nun wollen wir etwas anderes aufzeigen. Betrachten wir den folgenden Code, in dem wir ein Integer-Objekt 42 anlegen und dieses der Variablen x zuweisen. Danach weisen wir x der Variablen y zu. Dies führt dazu, dass nun beide Variablen dasselbe Objekt referenzieren:

Zwei Python-Variablen referenzieren das gleiche Objekt

Was wird passieren, wenn wir nun die Zuweisung y = 78 ausführen? Python wird zuerst ein neues Integer-Objekt mit dem Inhalt 78 erzeugen und dann wird die Referenz von y auf dieses Objekt geändert, wie wir im folgenden Bild sehen können:

Ein neues Objekt einer bestehenden Python-Variablen zuweisen

Höchst wahrscheinlich würden wir noch weitere Änderungen an den Variablen im Lauf unseres Programmes erfahren. Zum Beispiel könnte der Variablen x ein String zugewiesen werden. Das Integer-Objekt "42" wird damit verwaist. Es muss von Python entfernt werden, da es von keiner anderen Variablen referenziert wird.

Python-Variable, die ein Stringobjekt referenziert.

id-Funktion



Es stellt sich die Frage, wie man das oben gesagte überprüfen kann. Dazu bietet sich die Identitätsfunktion 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 = 42
>>> id(x)
10107136
>>> y = x
>>> id(x), id(y)
(10107136, 10107136)
>>> y = 78
>>> id(x), id(y)
(10107136, 10108288)
>>> 
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.

Gültige Variablennamen

Eine Bezeichner in Python ist eine Name um Variablen, Funktionen, Klassen, Module oder andere Objekte eindeutig zu benennen. Der Bezeichner kann folgende Zeichen beinhalten: Seit Python 3 wird Unicode unterstützt. Somit kann der Bezeichner auch Unicode-Zeichen enthalten. Die Länge eines Bezeichners ist nicht begrenzt. Es ist aber auf Groß- und Kleinschreibung zu achten. Folgende Variablen-Definitionen sind alle gültig:
>>> height = 10
>>> maximum_height = 100
>>> 
>>> υψος = 10
>>> μεγιστη_υψος = 100
>>> 
>>> MinimumHeight = 1


Python Schlüsselwörter

Abgesehen von den oben genannten Regeln für die Nutzung von Bezeichnern, dürfen diese nicht mit den folgenden Python-Schlüsselwörtern übereinstimmen: and, as, assert, break, class, continue, def, del, elif, else, except, False, finally, for, from, global, if, import, in, is, lambda, None, nonlocal, not, or, pass, raise, return, True, try, while, with, yield

Namens-Konventionen

Wir haben bereits festgestellt, dass wir manchmal Namen für Variablen oder Funktionen benötigen, die mehr als ein Wort beinhalten. Wir haben zum Beispiel "maximum_height" verwendet. Der Unterstrich fungiert hier als Wort-Trenner, denn Leerzeichen sind nicht möglich. Einige Entwickler nutzen gerne die s.g. Camel-Case-Schreibweise. Diese würden stattdessen "MaximumHeight" schreiben. Es ist wohl Geschmacksache welche Schreibweise man verwendet. Ich persönlich ziehe "die_natürliche_art" der "DieNatürlicheArt" vor. Der Style Guide für Python Code empfiehlt ebenfalls die Unterstrich-Schreibweise sowohl für Variablen, als auch für Funktionen.

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 Es gibt also verschiedene "Arten" von Objekten für verschiedene Datentypen. Schauen wir uns die verschiedenen Datentypen an, die bereits in Python integriert sind.

Zahlen

Python kennt die folgenden eingebauten (built-in) Datentypen für Zahlen:



Ganzzahlige Division

Die ganzzahlige Division (englisch: integer division) hat in früheren Versionen ständig zu Fehlern geführt. Im Folgenden teilen wir in einer Python2-Version die Zahl 10 durch 3.
bernd@telesto ~ $ python
Python 2.7.11+ (default, Apr 17 2016, 14:00:29) 
[GCC 5.3.1 20160413] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 10 / 3
3
>>> 
Auch wenn das Ergebnis den meisten Mathematikern und Informatikern logisch erscheinen wird, ist es nicht das, was die meisten Leute an dieser Stelle erwarten würden. Die zugrundeliegende Idee bei dem obigen Ergebnis ist nämlich, dass bei der Division eines Integers durch einen weiteren Integer, ein Integer-Wert als Ergebnis heraus kommt, d.h. dass eine Operation innerhalb ihrer Datentypen abgeschlossen ist. Dies wird mit einer zusätzlichen Rundung (floor division) erreicht. Im obigen Beispiel kann man es gut erkennen.

Um dies zu vermeiden nutzt Python 3 keine Rundung bei der Division (true division). Wenn Integer durch Integer dividiert werden, entstehen Float-Werte.
$ python3
Python 3.4.3 (default, Oct 14 2015, 20:28:29) 
[GCC 4.8.4] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> x = 11
>>> y = 3
>>> z = x / y
>>> type(z)
<class 'float'>
>>> print(z)
3.6666666666666665
Die Division mit Abschneiden ("floor division") kann aber in Python 3 mit "//" erzwungen werden.
$ python3
Python 3.4.3 (default, Oct 14 2015, 20:28:29) 
[GCC 4.8.4] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> z = 10 // 3
>>> type(z)
<class 'int'>
>>> print(z)
3
>>>


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 im Wesentlichen nur ein Traum. Heutzutage beschäftigen sich Computer in einem großen Umfang mit Textverarbeitung; die bedeutendsten 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 ein String - wie Text - eine Folge von Zeichen. Die Frage bleibt, was ein Zeichen (englisch character) darstellt. In einem Buch oder auch einem Text, wie beispielsweise dem, 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.

Der Buchstabe A in verschiedenen Fonts / Graphemen In der Computertechnik stellt ein Character eine Informationseinheit dar. Die Zeichen (englisch 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 Buchstabens "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.
Die meisten auf ASCII beruhenden Zeichenkodierungen, wie beispielsweise ISO-8859, sind allerdings auf 256 Bytes oder Zeichen beschränkt. ASCII selbst ist sogar auf 128 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 der erweiterte ASCII-Code auf 256 Zeichen pro Zeichensatz beschränkt ist, gibt es bei Unicode praktisch gesehen keinerlei Einschränkungen, denn mit 4 Bytes kann man 2564 Zeichen (also mehr als 4,29 Milliarden) 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 Eins-zu-eins umsetzen. Aber das wäre eine Speichervergeudung. Ein Textdokument würde dann viermal so viel Speicherplatz benötigen wie bisher unter ASCII.

Es gibt verschiedene Zeichencodierungen für Unicode:


Unicode-Kodierungen

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. verschiedene 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.

String Indizierung

Jedes einzelne Zeichen eines Strings, kann über einen Index angesprochen werden. Im folgenden Beispiel sehen wir, wie der im Bild dargestellte String in Python definiert wird und wie wir auf ihn zugreifen können:

>>> s = "Python"
>>> print(s[0])
P
>>> print(s[3])
h
Die 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öglichkeit. 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:

String Index negativ

Im Code sieht das wie folgt aus:
>>> s = "Python"
>>> last_character = s[-1]
>>> print(last_character)
n


Strings können unter Benutzung von angegeben werden.

Wichtige Stringfunktionen

Ein paar String-Funktionen:

Unveränderliche Zeichenketten

Wie in Java aber nicht wie in C oder C++, können Strings 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
>>> 


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
True
Wie 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
True
Aber 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
True
Also 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


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: Die Auswertung von Escape-Zeichen kann man verhindern, indem man einem String ein r oder R unmittelbar voranstellt.
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 in Unicode enthält, enthält ein Byte-Objekt eine Folge von Bytes mit den Werten 0 .. 255, die den ASCII-Werten entsprechen.
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 Strings ohnehin automatisch Unicode sind:
>>> txt = u"Geht nicht"
  File "<stdin>", line 1
    txt = u"Geht nicht"
                      ^
SyntaxError: invalid syntax
>>>