13.3 Unveränderliche Listen – tuple
Der Datentyp list ist sehr flexibel und wird häufig verwendet. Seine Mächtigkeit und Flexibilität haben aber auch den Nachteil, dass die Verwaltung einer Liste intern relativ ressourcenaufwendig ist. Oft wird gar nicht die Flexibilität einer Liste benötigt, sondern nur ihre Fähigkeit, Referenzen auf beliebige Instanzen zu speichern. Deshalb existiert in Python neben list der Datentyp tuple, der im Gegensatz zu list immutabel ist.
Der Datentyp tuple bringt keinen Mehrwert in Bezug auf Funktionalität, denn Listen können alles, was tuple leistet. Tatsächlich steht für tuple-Instanzen nur der Grundstock an Operationen für sequenzielle Datentypen bereit, wie er in Abschnitt 13.1 beschrieben wird.
Zum Erzeugen neuer tuple-Instanzen dienen die runden Klammern, die – wie bei den Listen –, durch Kommata getrennt, die Elemente des Tupels enthalten:
>>> a = (1, 2, 3, 4, 5)
>>> a[3]
4
Ein leeres Tupel wird durch zwei runde Klammern () ohne Inhalt definiert. Eine Besonderheit ergibt sich für Tupel mit nur einem Element. Versucht man, ein Tupel mit nur einem Element auf die oben beschriebene Weise zu erzeugen, ist der entstehende Programmtext unter Umständen nicht eindeutig:
>>> kein_tuple = (2)
>>> type(kein_tuple)
<class 'int'>
Mit (2) wird keine neue tuple-Instanz erzeugt, weil die Klammer in diesem Kontext schon für die Verwendung in Rechenoperationen für Ganzzahlen verwendet wird. Das Problem wird umgangen, indem in Literalen für Tupel mit nur einem Element diesem Element ein Komma nachgestellt werden muss:
>>> ein_tuple = (2,)
>>> type(ein_tuple)
<class 'tuple'>
13.3.1 Tuple Packing/Unpacking und Sequence Unpacking
Es ist möglich, die umschließenden Klammern bei einer tuple-Definition entfallen zu lassen. Trotzdem werden die durch Kommata getrennten Referenzen zu einem tuple zusammengefasst, was man Tuple Packing nennt:
>>> datum = 26, 7, 1987
>>> datum
(26, 7, 1987)
Umgekehrt ist es möglich, die Werte eines Tupels wieder zu entpacken:
>>> datum = 26, 7, 1987
>>> (tag, monat, jahr) = datum
>>> tag
26
>>> monat
7
>>> jahr
1987
Dieses Verfahren heißt Tuple Unpacking, und auch hier können die umschließenden Klammern entfallen. Durch Kombination von Tuple Packing und Tuple Unpacking ist es sehr elegant möglich, die Werte zweier Variablen ohne Hilfsvariable zu tauschen oder mehrere Zuweisungen in einer Zeile zusammenzufassen:
>>> a, b = 10, 20
>>> a, b = b, a
>>> a
20
>>> b
10
Richtig angewandt, kann die Nutzung dieses Features zur Lesbarkeit von Programmen beitragen, da das technische Detail der Zwischenspeicherung von Daten hinter die eigentliche Absicht, die Werte zu tauschen, zurücktritt.
Mithilfe von Tuple Unpacking können auch Werte am Anfang und am Ende einer Sequenz ausgelesen werden. Betrachten wir das folgende Beispiel:
>>> zahlen = [11, 18, 12, 15, 10]
>>> elf, *andere, zehn = zahlen
>>> elf
11
>>> zehn
10
>>> andere
[18, 12, 15]
Wird beim Tuple Unpacking einer Referenz ein Stern * vorangestellt, werden in dieser alle übrigen Werte der Sequenz gespeichert. Im Beispiel oben werden der erste Wert von zahlen in elf und der letzte Wert in zehn abgelegt. Die Zahlen dazwischen werden in andere gesammelt.
Vor und nach dem Eintrag mit Stern kann eine beliebige Anzahl anderer Referenzen stehen. Insbesondere kann der erste oder letzte Eintrag einen Stern haben:
>>> zahlen = [11, 17, 17, 19, 10]
>>> *irgendwas, neunzehn, zehn = zahlen
>>> neunzehn
19
>>> zehn
10
>>> elf, *blablabla = zahlen
>>> elf
11
>>> blablabla
[17, 17, 19, 10]
Es kann in einer Zuweisung mit Tuple Unpacking immer nur genau eine Referenz mit einem Stern geben. Dies ist auch sinnvoll, da ansonsten Mehrdeutigkeiten entstehen können.
[»] Hinweis
Generell ist Vorsicht geboten, wenn Unpacking für ungeordnete Datentypen verwendet wird. Im folgenden Beispiel hängt die Reihenfolge der Elemente 1, 2, 3 davon ab, in welcher Reihenfolge über die Menge {3,1,2} iteriert wird:
>>> a, b, c = {3, 1, 2}
>>> a, b, c
(1, 2, 3)
Da diese Reihenfolge ein Implementierungsdetail ist, kann sie sich zwischen verschiedenen Python-Versionen unterscheiden. Näheres zu Mengen erfahren Sie in Kapitel 15.
13.3.2 Immutabel heißt nicht zwingend unveränderlich!
Auch wenn tuple-Instanzen immutabel sind, können sich die Werte der enthaltenen Elemente der Erzeugung ändern. Bei der Erzeugung eines neuen Tupels werden die Referenzen festgelegt, die es speichern soll. Verweist eine solche Referenz auf eine Instanz eines mutablen Datentyps, etwa eine Liste, kann sich dessen Wert trotzdem ändern:
>>> a = ([],)
>>> a[0].append("Und sie dreht sich doch!")
>>> a
(['Und sie dreht sich doch!'],)
Die Unveränderlichkeit eines Tupels bezieht sich also nur auf die enthaltenen Referenzen und ausdrücklich nicht auf die dahinterstehenden Instanzen.
Dass Tupel immutabel sind, ist also keine Garantie dafür, dass sich Elemente nach der Erzeugung des Tupels nicht mehr verändern.