Python-Programmierung:Datentypen
Aus Wikibooks
»Everything is an Object« - Alles sind Objekt - Dies ist das grundlegende Pythonparadigma. Egal ob man mit einer Zeichenkette, einer ganzen Zahl, einer Liste, einem Objekt einer selbstentwickelten Klasse operiert, alles sind Objekte. So überraschend vielleicht klingen mag, aber auch Funktionen und selbst Klassen sind Objekte.
Dieses Kapitel und seine Unterabschnitte stellt die verschiedenen Objektklassen vor, mit denen man sich in Python konfrontiert sieht.
Inhaltsverzeichnis |
[Bearbeiten] Objekte
Jedes »Ding« (Entität), mit dem man in Python umgeht, ist ein Objekt. Aus dieser Eigenschaft ergeben sich eine ganze Reihe von Konsequenzen, auf die wir in späteren Abschnitten und Kapiteln, insbesondere, bei der Erörterung der Objektorientierung a la Python noch genauer eingehen werden. Eine Eigenschaft lässt sich aber bereits jetzt gut erklären und für unsere Beispiele nutzen, die Funktion type.
Was sind das für Objekte mit denen Python arbeitet? Ohne der Erörterung der Objektorientierung vorgreifen zu wollen, kann man - sehr informell - sagen, dass alles, was in irgend einer Weise »Daten« oder »Werte« darstellt, Objekte sind. So sind Zahlen (ganze Zahlen, Fließpunktzahlen, Wahrheitswerte oder auch komplexe Zahlen) ebenso Objekte, wie eine Liste oder ein Button einer GUI. Objekte sind die Dinge, mit denen man etwas machen kann, d.h. mit ihnen lässt sich rechnen, verarbeiten, zuweisen, traversieren und alles mögliche andere. Operationen, die man mit oder auf Objekten ausführt, nennen sich Methoden.
[Bearbeiten] Einfache Reflexion - Die Funktion type()
Sie liefert zu jedem als Argument übergebenen »Ding« (Objekt) dessen Art zurück und bietet damit eine einfache Form der Reflexion
>>> type(7) # Eine Zahl ist ein Objekt <type 'int'> >>> type(11223344556677889900) <type 'long'> >>> type("Ich bin eine Zeichenkette") # Zeichenkette <type 'str'> >>> type(lambda a, b : a*b) # Eine anonyme Funktion <type 'function'> >>> def EineFunktion(): ... print "Ich bin eine Funktion" ... >>> type(EineFunktion) # Funktionen sind auch Objekte <type 'function'> >>> type([1, 2, 3, 4]) # Eine Liste mit vier Elementen <type 'list'> >>> type(len) # Eingebaute Funktionen <type 'builtin_function_or_method'> >>> type(type) # Selbst type ist ein Objekt <type 'type'> >>> type(type(7)) # Python Esotherik: Die Rückgabewerte von type sind selbst wieder type-Objekte <type 'type'>
Die ersten Beispiele der kleinen interaktiven Pythonsitzung dürften selbsterklärend sein. Ganze Zahlen werden, je nach Größe, als int oder long Objekte gespeichert. Das lambda-Konstrukt erzeugt eine anonyme Funktion, d.h. genauer ein anonymes Funktionsobjekt, denn auch Funktionen sind Objekte, wie auch die selbstdefinierte Funktion EineFunktion zeigt. Auch Containerdatentypen, wie Listen, sind Objekte. Mit den eckigen Klammern ([]) lassen sich in Python anonyme Listen (arrays) erzeugen. die natürlich ebenfalls Objekte (Listenobjekte) darstellen.
len ist eine der wenigen fest in den Pythoninterpreter eingebauten Funktionen (sie liefert die Länge von Listen zurück).
[Bearbeiten] Das Objekt type
- Fortgeschrittener Inhalt: Der nachfolgende Unterabschnitt beschreibt tiefer gehende Konzepte Pythons, deren Verständnis für eine einfache Nutzung der Sprache nicht wirklich kriegsentscheidend sind und deswegen auch ohne Probleme übersprungen werden können.
Wenn wir noch einmal einen Blick auf die beiden letzten Beispiele werfen, sollte uns etwas überraschendes auffallen:
>>> type(type) # Selbst Type ist ein Objekt <type 'type'> >>> type(type(7)) # Python Esotherik: Die Rückgabewerte von Type sind selbst wieder Type-Objekte <type 'type'>
Die eingebaute Funktion type scheint alles andere als eine eingebaute Funktion zu sein, sondern ein Objekt der Klasse type, dem Type-Objekt. Auch die Ergebnisse, die type liefert sind, Type-Objekte. Was als Ausgabe tatsächlich auf dem Bildschirm erscheint, z.B. <type 'str'>, ist die nicht formale Stringrepräsentation.
Das Type-Objekte enthält den Mechanismus, mit dem in Python Klassen definiert werden. Ein Beispiel aus der Pythondokumentation verdeutlicht dies:
>>> class X(object): ... a = 1 ... >>> X = type('X', (object,), dict(a=1))
Beide Ausdrücke sind gleichwertig, d.h. sie erzeugen das gleiche Objekt: die Klasse X. Klassen sind in Python, wie alles andere auch, ebenfalls Objekte. Das class Statement bewirkt nichts anderes, dass ein neues Klassenobjekt erzeugt wird und eine Referenz darauf im Namen der Klasse abgelegt wird. Mehr zu diesem Thema im Kapitel Klassen und Objekte.
[Bearbeiten] Typen
Wenn wir schon über Typen diskutieren, drängt sich zwangsläufig die Frage auf, welche (Daten-)Typen einem in Python überhaupt zur Verfügung stehen. Eine vollständige und formale Liste liefert die Python Language Reference. Die Typen in Python spannen eine Typenhierachie auf, so finden sich etwa Wahrheitswerte, Zahlen im Hierachieteilbaum numbers.Number, der sich in die Unterknoten numbers.Integral, für ganzzahlige und boolsche Werte, numbers.Real für Fließkommazahlen und numbers.Complex untergliedert.
Ein detaillierteres Bild liefern die Unterkapitel zu den einzelnen Typen:
- Der Nullwert

- Wahrheitswerte

- Zahlen

- Zeichenketten

- Containertypentypen (Liste, Mengen, Abbildungen)

- Sonstiges

Daneben gibt es natürlich auch die selbstdefinierten Objektklassen, die im Kapitel Klassen und Objekte ausgiebig erörtert werden.
[Bearbeiten] Variablen, Namen und Referenzen
Wie die meisten Programmiersprachen kennt auch Python Variablen, denen sich Werte zuweisen lassen. Das Gleichheitszeichen (=) dient dabei als Bindungsoperator. Das Ergebnis der Auswertungs des Ausdrucks auf der rechten Seite des Gleichheitszeichen wird an den Namen auf der linken Seite gebunden. Gebunden? Nicht zugewiesen?
Python kennt keine Zuweisung, wie in Pascal oder C, in denen der Wert eines Ausdrucks in einer Variablen gespeichert wird. Python kennt nur Referenzen. Wie mehrfach erwähnt sind alle Dinge in Python Objekte:
a = 7
a ist daher keine Variable sondern ein Name an den ein int Type(-objekt) gebunden wurde. Dieser Unterschied zu anderen Sprachen ist für das Verständnis von Python sehr wichtig: (Variablen-)Namen in Python enthalten Referenzen auf Werte und nicht die Werte selbst.
In den meisten Fällen spielt dieser Unterschied keine Rolle. Namen in Python verhalten sich wie Variablen und können auch so eingesetzt werden.
>>> a = 100 >>> b = 200 >>> c = b / a >>> a, b ,c (100, 200, 2) >>> a, b = b, a # Vertauschen ohne Hilfsvariable >>> a, b, c (200, 100, 2) >>> type(a) <type 'int'>
Bemerkbar macht sich der Unterschied, wenn wir mit komplexeren Objekten hantieren, z.B. mit einem Containerobjekt, wie der Liste:
>>> a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] # Eine Liste wird an den Namen a gebunden >>> type(a) # a verweist auf eine Liste <type 'list'> >>> b = a # Der Name b wird mit der gleichen Liste verbunden >>> b [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] >>> b[3] = 4711 # Die Liste wird geändert >>> b [1, 2, 3, 4711, 5, 6, 7, 8, 9, 10] >>> a # a und b verweisen auf die gleiche Liste [1, 2, 3, 4711, 5, 6, 7, 8, 9, 10] >>> a is b # Sie sind gleich True
Der obige Codeblock verdeutlicht nochmals das Konzept der Referenzen und Namensbindung. Der Ausdruck [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] erzeugt eine anonyme Liste (mutable sequence) mit 10 Elementen. Das Ergebnis der Auswertung dieses Ausdrucks ist daher eine Referenz auf ein Python Listenobjekt, dass durch den Bindungsoperator (=) an den Namen a gebunden wird. Was macht und welche Wirkung hat jetzt b = a? Gerade Pythonanfänger erwarten, dass mit dieser Zuweisung ein neues Objekt b entsteht, dass die gleichen Elemente wie a enthält. Das ist aber nicht der Fall, da es sich in Wirklichkeit in Python um gar keine Zuweisung handelt. Was passiert also genau?
Wie bei allen Bindungsoperationen wird als erstes der Ausdruck auf der rechten Seite ausgewertet, also a. a enthält eine Referenz auf ein Listenobjekt. Die Auswertung liefert also die Referenz, die nun an den Namen b gebunden wird. Mit anderen Worten: a und b verweisen auf das identische Objekt. Oder anders ausgedrückt: Unsere Liste ist schlicht und ergreifend unter zwei verschiedenen Namen erreichbar, wie der Rest des Codeblocks zeigt.
[Bearbeiten] Binden, flache Kopie und tiefe Kopie / Binding, shallow copy und deep copy
Da Python ausschließlich mit Referenzen arbeitet, ist es sehr wichtig, genau zu wissen, was, d.h. welche Wirkung, man in einer bestimmten Situation erreichen will:
- Möchte ich das gleiche Objekt unter einem weiteren Namen erhalten (binding)?
- Möchte ich ein neues Objekt (Kopie) mit den gleichen Elementen erhalten (shallow copy)?
- Möchte ich ein neues Objekt mit Kopien der Elemente erhalten (deep copy)?
Worin unterscheiden sich die letzten beiden Varianten von der Ersten? Es gibt Fälle, in denen der Bindungsoperator (=) und ein anderer Name für ein bestehendes Objekt nicht ausreicht, sondern man eine echte Kopie eines Objekts, z.B. einer Liste benötigt.
[Bearbeiten] shallow copy
Die einfachst Möglichkeit, ein Duplikat einer Liste zu erhalten, ist eine neue, mit den gleichen Elementen zu erzeugen:
>>> a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] >>> b = a[:] # Erzeugt eine neue Liste mit den Elementen von a >>> a [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] >>> b [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] >>> b[4] = 4711 >>> b [1, 2, 3, 4, 4711, 6, 7, 8, 9, 10] >>> a [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] >>> a is b False >>> import copy >>> b = copy.copy(a) # shallow copy - Bibliotheksfunktion
b enthält eine flache (shallow) Kopie von a, d.h. die Elemente von b sind die gleichen, wie die von a. Es mag ein wenig wie Wiederkäuen klingen, aber der Punkt ist wichtig: b = a[:] erzeugt nur ein Kopie von a nicht aber eine Kopie der Elemente von a.
[Bearbeiten] deep copy
>>> a = [ 1, 2, 3, [ 11, 22, 33 ] ] >>> a [1, 2, 3, [11, 22, 33]] >>> b = a[:] >>> b [1, 2, 3, [11, 22, 33]] >>> b[3][0] = 77 >>> b [1, 2, 3, [77, 22, 33]] >>> a [1, 2, 3, [77, 22, 33]] >>> b[0] = 11 >>> b [11, 2, 3, [77, 22, 33]] >>> a [1, 2, 3, [77, 22, 33]]
In diesem Beispiel ist a eine Liste mit 4 Elementen, wovon das 4. Element selbst wieder eine Referenz auf eine Liste ist. Listen enthalten nur Referenzen auf ihre Elementobjekte. Durch die Kopie (a[:]) wurden nur die Referenzen von a in eine neue Liste kopiert, allerdings keine Kopien der Elemente selbst erzeugt. Eine Operation, die dieses bewerkstelligt nennt sich deep copy und findet sich unter einem entsprechenden Namen im Modul copy</copy>.
>>> import copy
>>> a = [ 1, 2, 3, [ 11, 22, 33 ] ]
>>> b = copy.deepcopy(a)
>>> a
[1, 2, 3, [11, 22, 33]]
>>> b
[1, 2, 3, [11, 22, 33]]
>>> b[3][0] = 77
>>> b
[1, 2, 3, [77, 22, 33]]
>>> a
[1, 2, 3, [11, 22, 33]]
[Bearbeiten] Weitere bindende Operatoren und Statements
Außer dem Gleichheitszeichen (=) gibt es noch andere Operationen, die Objekte an Namen binden.
[Bearbeiten] Das def Statement
def Statement>>> def EineFunktion(): # Eine Funktion wird definiert ... """ Dies ist eine Funktion """ ... print "Ich bin eine Funktion" ... >>> print EineFunktion <function EineFunktion at 0x7f68c1c0a6e0> >>> type(EineFunktion) <type 'function'> >>> EineFunktion() Ich bin eine Funktion >>> dieGleicheFunktion = EineFunktion # neuer Name, gleiche Funktion >>> dieGleicheFunktion <function EineFunktion at 0x7f68c1c0a6e0> >>> dieGleicheFunktion() Ich bin eine Funktion >>> dieGleicheFunktion is EineFunktion True
Informell betrachtet, definiert das def Statement eine Funktion: Nach einer, möglicherweise leeren Argumentliste, folgt ein Doppelpunkt, der einen Block einleitet - Den Funktionskörper. Formal sind die Abläufe etwas komplexer, denn auch Funktionen sind, wie alles in Python, Objekte - Funktionsobjekte. def macht also nichts anderes, als aus dem Funktionskörper ein Funktionsobjekt zu instanziieren und an den Funktionsnamen zu binden. Damit verhalten sich Funktionen, wie alle anderen Objekte auch. Man kann sie zuweisen, als Argument an Funktionen übergeben oder auch in Listen oder Hashes ablegen. Im obigen Beispiel lässt sich nach der Zuweisung dieGleicheFunktion = EineFunktion nicht unterscheiden, welches die ursprüngliche Funktion war, da beide Namen auf das gleiche Funktionsobjekt verweisen.
[Bearbeiten] Das class Statement
Nach dem, was bisher über Namen und Objekte erzählt wurde, dürfte es nicht sonderlich überraschen, dass auch das class Statement, mit dem Klassen definiert werden, ebenfalls ein Objekt an einen Namen bindet, nämlich ein Klassenobjekt an dessen Klassennamen, d.h. Klassen sind Objekte. Und so, wie man mit Funktionen alles machen kann, was man mit Objekten machen kann, kann man dies auch mit Klassen. Klassen lassen sich anderen Namen zuweisen, als Argument an eine Funktion übergeben oder in anderen Objekten ablegen.
[Bearbeiten] Namensräume und Sichtbarkeitsbreiche
Wie lange ist eine Variable bzw. die Bindung eines Objektes an einen Namen gültig? Auf welchen Namen greife ich eigentlich zu?
In Gegensatz zu vielen anderen Sprachen sind die Regeln für Gültigkeitsbereiche eher einfach. Um sie aber zu verstehen, ist es hilfreich, das Konzept der Python Namensräume (Namespace) zu verstehen.
[Bearbeiten] Namensräume (namespaces)
Ein Namensraum ist die Abbildungen von Namen auf Objekte. Wenn ein Objekt an einen Namen gebunden wird, muss diese Bindung schließlich irgendwo gespeichert werden.
>>> a = [ 1, 2, 3 ] >>> b = "Ich bin ein String"
Irgend ein Mechanismus muss für die Abbildung zwischen Namen und und den an den Namen gebundenen Objekten sorgen. Dies sind die Namensräume. Allgemein sorgen Python-Verzeichnisse (dict) für diese Abbildung, obwohl dies für den Anwender im Allgemeinen völlig unbemerkt geschieht. Namespaces gibt es eine Menge, beispielsweise den der eingebauten Funktionen, wie z.B. abs(). Aber auch jede Funktion und jedes Modul besitzt einen Namensraum. Alle Namensräume sind voneinander unabhängig. Ein Name a in einem Namensraum besitzt keinerlei Verbindung zu einem Namen a in einem anderen. So können durchaus zwei Module Funktionen oder Klassen gleichen Namens enthalten, ohne, dass es zu Konflikten kommt.
Namensräume haben eine Lebensdauer. So wird der Namensraum der eingebauten Funktionen beim Start des Interpreters erzeugt und lebt so lange, wie der Interpreter läuft. Der Namensraum einer Funktion entsteht hingegen erst mit ihrem Aufruf und existiert, bis die Funktion wieder verlassen wird. Namensräume von Modulen werden beim Importieren erzeugt.
[Bearbeiten] Sichtbarkeitsbereiche (Scope)
Ein Scope ist ein Bereich im Quelltext eines Pythonprogramms, in dem auf einen Namespace direkt zugegriffen werden kann. Das klingt komplizierter als es ist. Ein Beispiel soll das Konzept daher verdeutlichen:
>>> a = 1 >>> def EineFunktion(): ... a = 4711 ... print "\'a\' in der Funktion", a ... >>> print a 1 >>> EineFunktion() 'a' in der Funktion 4711 >>> a 1
In diesem Beispiel wird mit zwei Namensräumen gearbeitet: dem globalen Namensraum des Moduls in dem sich der Programmcode befindet und der Namensraum der Funktion EineFunktion. Bei der Zuweisung in der ersten Zeile an den Namen a ist der Scope der Namensraum des Moduls. Wie oben schon erwähnt, ist ein Scope ein Textbereich innerhalb des Programmtextes, während die Namensräume während des Programmlaufs existieren. So umfasst der lokale Scope der Funktion EineFunktion die beiden Zeilen des Funktionskörpers. Anschließend befindet sich das Programm wieder im globalen Scope des Moduls.
Python verwendet immer den innersten Namensraum, sozusagen, den als erstes zu erreichenden Namensraum.
[Bearbeiten] Das global Statement
Manchmal kann es sinnvoll sein, den Namensraum zu dem ein Name gehört explizit zu ändern:
>>> a = 1 >>> def EineAndereFunktion(): ... global a ... print a ... a = 4711 ... >>> print a 1 >>> EineAndereFunktion() 1 >>> print a 4711
Dieses Beispiel zeigt eine etwas abgewandelte Version des vorherigen. Der Unterschied besteht in einer zusätzlichen Zeile mit dem Statement global, durch den der Namensraum des Namens a auf den modulglobalen Namensraum umgebogen wird.
[Bearbeiten] globals() und locals()
Mit diesen beiden eingebauten Funktionen lassen sich die Inhalte der jeweiligen Namensräume abfragen. Der nachfolgende Sitzungsmitschnitt zeigt den Inhalt des globalen Namensraum der beiden vorangegangenen Beispiele:
>>> globals() {'a': 4711, '__builtins__': <module '__builtin__' (built-in)>, '__file__': '/etc/pythonstart', 'EineFunktion': <function EineFunktion at 0x7f887cb276e0>, 'EineAndereFunktion': <function EineAndereFunktion at 0x7f887cb277d0>, '__name__': '__main__', '__doc__': None}
Der Rückgabewert von globals und locals ist ein Dictionary mit den Namen des jeweiligen Namespaces als Schlüssel (key) und dem an den Namen gebundenen Objekts als Wert. Abgesehen von ein paar Spezialeinträgen (__builtins__, __file__, __name__ und __doc__) enthält der globale Namespace genau das, womit zu rechnen war: den Namen a für die ganze Zahl und die beiden Funktionen EineFunktion und EineAndereFunktion; das def Statement bindet die Funktionsobjekte an den Funktionsnamen. Da beide Funktionen im modulglobalen Scope definiert wurden, finden sie sich selbstverständlich auch im modulglobalen Namespace wieder.
[Bearbeiten] Der Punkt (.) als Namespacequalifizierer
In alten Pythonprogrammen kann man oft eine echte Unsitte beobachten, den Wildcardimport von Modulen:
from Tkinter import *
Warum ist von diesem Konstrukt abzuraten? Nach allem, was wir inzwischen über Namen, Bindungen und Namespaches wissen, dürfte es klar sein, was bei dieser Anweisung passiert: Durch das from Statement, wird das nachfolgende Modul in dem Interpreter importiert. Wie erwähnt, wird für jedes Modul ein eigener Namespace erzeugt. Namen aus Tkinter haben somit keinen Einfluss auf Namen im Namespace des Moduls, dass das from Statement ausführt, wäre da nicht das nachfolgende import *, dass bewirkt, dass der komplette Namespace[1] in den eigenen Namespace importiert wird, was meist nicht das ist, was man beabsichtigt und leicht zu sehr schwer zu identifizierenden Fehlern führen kann.
Zum Glück gibt es zwei Alternativen zum vollständigen Import. Zum einen lassen sich die zu importierenden Namen dadurch begrenzen, dass man nur die Namen nach import angibt, die man wirklich benötigt. Die beste Alternative stellt allerdings die Verwendung des Namespacequalifizierers, dem Punkt (.) dar.
import Tkinter # Import myLabel = Tkinter.Label(text="Hallo Welt") # Verwendung mit Namespacequalifizierung
Bei dieser Methode wird nur der Modulname importiert. Genau genommen nimmt import eine Namesbindung vor. Nachdem das Modul importiert und vom Interpreter zu einem Modulobjekt verarbeitet wurde, wird die Referenz darauf an einen gleichlautenden Namen wie dem des Moduls gebunden und in den aktuellen Namespace aufgenommen. Sollte der Name ungünstig sein, etwa, weil er mit bestehenden Namen kollidiert, lässt sich mit as Abhilfe schaffen.
import Tkinter as DieTkGUI # Modul unter einem anderem Namen importiert myLabel = DieTkGUI.Label(text="Hallo Welt")
[Bearbeiten] Garbage Collection
- ↑ Module verfügen über Möglichkeiten, die durch einen Wildcardimport importierten Namen festzulegen.