14.6. Tupel - tuple#

Listen und Tupel tuple (wie auch Zeichenketten, und Zahlenbereiche) sind allesamt Sequenzen und haben dadurch sehr ähnliche Eigenschaften. Einzig bei der Erstellung verwenden wir statt der eckigen die runden Klammern:

numbers = (1, 2, 3, 4, 5)
numbers
(1, 2, 3, 4, 5)

Die Indexierung ist identisch zur Indexierung von Listen.

numbers = (1, 2, 3, 4, 5)
print(numbers[2:4])
(3, 4)

Der große Unterschied lieft in der Veränderbarkeit, denn Tupel tuple sind unveränderlich (engl. immutable)!

Unveränderlichkeit

Im Gegensatz zu Listen sind Tupel unveränderliche Datenstrukturen.

Aber Vorsicht ist geboten, denn das bedeutet nicht, dass wir ein Tupel überhaupt nicht verändern können. Was wir jedoch nicht verändern können sind die Adressen bzw. ids seiner Elemente!

Folgender Code führt zu einem Fehler:

numbers = (1, 2, 3, 4, 5)
numbers[2] = -10
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[3], line 2
      1 numbers = (1, 2, 3, 4, 5)
----> 2 numbers[2] = -10

TypeError: 'tuple' object does not support item assignment

Doch können wir eine Datenstruktur, die Element eines Tupels ist, durchaus verändern:

numbers = (1, 2, [3, 4, 5])
numbers[2][0] = 'a'
numbers
(1, 2, ['a', 4, 5])

Tupel werden oft verwendet wenn wir eine Ansammlung von heterogenen Datentypen zusammenfassen wollen, und wir explizit ausdrücken bzw. sicher gehen wollen, dass dieses Tupel nicht verändert wird.

Um beispielsweise das sich nicht ändernde Alphabet zu modellieren, eignet sich ein Tupel:

alphabet = ('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z')
alphabet
('A',
 'B',
 'C',
 'D',
 'E',
 'F',
 'G',
 'H',
 'I',
 'J',
 'K',
 'L',
 'M',
 'N',
 'O',
 'P',
 'Q',
 'R',
 'S',
 'T',
 'U',
 'V',
 'W',
 'X',
 'Y',
 'Z')

Python bietet eine etwas hässliche Abkürzung für die Erstellung eines Tupels mit nur einem Element:

singleton = 1,
singleton
(1,)

entspricht

singleton = (1,)
singleton
(1,)

Achtung

number = (1)
number
1

funktioniert nicht!

Python bietet auch das Konzept des tuple-packing und tuple-unpacking, was enorm praktisch ist:

triple = (1, 2, 3)
x, y, z = triple  # unpacking
print(f'x = {x}')
print(f'y = {y}')
print(f'z = {z}')
packing = x, y, z # packing
print(f'packing = {packing}')
x = 1
y = 2
z = 3
packing = (1, 2, 3)

Ziemlich cool, wie wir finden. Packing bzw. unpacking ist insbesondere praktisch, wenn Sie eine Funktion schreiben möchten, die mehr als ein Rückgabewert hat:

import random as rnd
def random_rgb_color():
    r = rnd.randint(0, 255)
    g = rnd.randint(0, 255)
    b = rnd.randint(0, 255)
    return r, g, b

r, g, b = random_rgb_color()
print(f'red = {r}, green = {g}, blue = {b}')
red = 101, green = 133, blue = 123

Im Falle der Listen hat der +=-Operator die Liste verändert. Was glauben Sie macht der +=-Operator für Tupel? Ist er definiert? Testen wir es aus:

numbers = (1, 2, 3, 4, 5)
numbers += (6,7,8)
numbers
(1, 2, 3, 4, 5, 6, 7, 8)

Kein Fehler! Wird nun doch numbers verändert? Ja und nein. Der Speicherbereich des ursprünglichen Tupels bleibt unverändert, stattdessen wird eine Kopie angelegt.

numbers = (1, 2, 3, 4, 5)
numbers_copy = numbers
print(f'id before += {id(numbers)}')
numbers += (6,7,8)
print(f'id after += {id(numbers)}')
print(f'id after += of the original {id(numbers_copy)}')
print(f'numbers: {numbers}')
print(f'numbers_copy: {numbers_copy}')
id before += 4436090992
id after += 4444550720
id after += of the original 4436090992
numbers: (1, 2, 3, 4, 5, 6, 7, 8)
numbers_copy: (1, 2, 3, 4, 5)

Diese Inkonsistenz kann gefährlich sein, denn obwohl die Syntax der Listenverkettung und Tupelverkettung sehr ähnlich sind, ist ihre Semantik sehr verschieden.

Exercise 14.3 (Effizienzbetrachtung von Tupeln und Listen)

Welche der folgenden Code-Teile benötigt nach Ihrer Meinung mehr Computerressourcen und warum?

Listenerzeugung durch Anhängen:

numbers = []
i = 0
while i < 100:
    numbers += [i]
    i += 1
numbers

Tupelerzeugung durch Anhängen:

numbers = tuple()
i = 0
while i < 100:
    numbers += (i,)
    i += 1
numbers