Python & AI Tutorials Logo
Python Programmierung

15. Tupel und range-Objekte: Einfache unveränderliche Sequenzen

In Kapitel 14 haben wir Listen untersucht—Pythons vielseitigen, veränderbaren Sequenztyp. Jetzt betrachten wir zwei weitere wichtige Sequenztypen: Tupel (tuple) und range-Objekte (range). Während Listen hervorragend dafür geeignet sind, Sammlungen zu speichern, die sich im Laufe der Zeit verändern, bieten Tupel unveränderliche Sequenzen, die Daten vor Änderungen schützen, und range-Objekte bieten speichereffiziente Möglichkeiten, Zahlenfolgen darzustellen.

Wenn Sie verstehen, wann Sie welchen Sequenztyp verwenden, werden Ihre Programme effizienter, sicherer und in ihrer Absicht klarer. Am Ende dieses Kapitels wissen Sie, wie Sie effektiv mit Tupeln und range-Objekten arbeiten, und Sie verstehen die gängigen Operationen, die über alle Python-Sequenztypen hinweg funktionieren.

15.1) Tupel erstellen und verwenden (Die Bedeutung des Kommas)

Ein Tupel (tuple) ist eine geordnete, unveränderliche Sequenz von Elementen. Wie Listen können Tupel beliebige Datentypen enthalten und die Reihenfolge der Elemente beibehalten. Anders als bei Listen können Sie jedoch nach dem Erstellen eines Tupels dessen Inhalte nicht mehr verändern.

Tupel mit Klammern erstellen

Die häufigste Methode, ein Tupel zu erstellen, besteht darin, kommagetrennte Werte in runde Klammern zu setzen:

python
# Ein Tupel mit Testergebnissen von Schülern
scores = (85, 92, 78, 95)
print(scores)  # Output: (85, 92, 78, 95)
print(type(scores))  # Output: <class 'tuple'>
 
# Ein Tupel mit gemischten Datentypen
student_info = ("Alice", 20, "Computer Science", 3.8)
print(student_info)  # Output: ('Alice', 20, 'Computer Science', 3.8)
 
# Ein leeres Tupel
empty = ()
print(empty)  # Output: ()
print(len(empty))  # Output: 0

Tupel verwenden runde Klammern () als Literal-Syntax, während Listen eckige Klammern [] verwenden. Diese visuelle Unterscheidung hilft Ihnen, sofort zu erkennen, mit welchem Typ Sie arbeiten.

Das Komma erzeugt das Tupel, nicht die Klammern

Hier ist ein entscheidendes Detail, das viele Anfänger überrascht: Das Komma ist es, das tatsächlich ein Tupel erzeugt, nicht die Klammern. Die Klammern sind oft optional und dienen hauptsächlich dazu, das Tupel sichtbarer zu machen oder es in Ausdrücken zu gruppieren.

python
# Diese erzeugen alle das gleiche Tupel
coordinates_1 = (10, 20)
coordinates_2 = 10, 20  # Keine Klammern nötig!
print(coordinates_1)  # Output: (10, 20)
print(coordinates_2)  # Output: (10, 20)
print(coordinates_1 == coordinates_2)  # Output: True
 
# Das Komma ist entscheidend
x = (42)  # Das ist nur die Ganzzahl 42 in Klammern
y = (42,)  # Das ist ein Tupel, das ein Element enthält
print(type(x))  # Output: <class 'int'>
print(type(y))  # Output: <class 'tuple'>
print(y)  # Output: (42,)

Die Klammern in (42) sind nur Gruppierungsklammern, wie in mathematischen Ausdrücken. Um ein Tupel mit einem einzigen Element zu erstellen, müssen Sie ein nachgestelltes Komma verwenden: (42,). Dieses Komma sagt Python, dass Sie ein Tupel möchten und nicht nur einen gruppierten Ausdruck.

Wann Klammern erforderlich sind

Obwohl das Komma das Tupel erzeugt, werden Klammern in bestimmten Situationen notwendig, um Mehrdeutigkeiten zu vermeiden:

python
# Ohne Klammern wäre das verwirrend
def get_dimensions():
    return 1920, 1080  # Gibt ein Tupel zurück
 
width, height = get_dimensions()
print(f"Screen: {width}x{height}")  # Output: Screen: 1920x1080
 
# Klammern nötig, wenn Tupel als Funktionsargumente übergeben werden
print((1, 2, 3))  # Output: (1, 2, 3)
# Ohne Klammern würde Python drei separate Argumente sehen
 
# Klammern nötig bei komplexen Ausdrücken
result = (10, 20) + (30, 40)  # Tupel-Verkettung
print(result)  # Output: (10, 20, 30, 40)

Tupel mit einem Element erstellen

Die Anforderung des nachgestellten Kommas bei Tupeln mit einem Element bringt Anfänger oft aus dem Konzept:

python
# Häufiger Fehler: das Komma vergessen
not_a_tuple = ("Python")
print(type(not_a_tuple))  # Output: <class 'str'>
print(not_a_tuple)  # Output: Python
 
# Korrekt: das nachgestellte Komma hinzufügen
is_a_tuple = ("Python",)
print(type(is_a_tuple))  # Output: <class 'tuple'>
print(is_a_tuple)  # Output: ('Python',)
 
# Das Komma funktioniert sogar ohne Klammern
also_a_tuple = "Python",
print(type(also_a_tuple))  # Output: <class 'tuple'>
print(also_a_tuple)  # Output: ('Python',)

Warum verlangt Python diese scheinbar unbeholfene Syntax? Weil Klammern in Python bereits eine andere Bedeutung haben—sie gruppieren Ausdrücke. Ohne das Komma kann Python nicht zwischen (42) als gruppierter Zahl und (42) als Tupel unterscheiden.

Auf Tupel-Elemente zugreifen

Tupel unterstützen dieselben Indexierungs- und Slicing-Operationen wie Listen:

python
# Tupel mit Schülerinformationen
student = ("Bob", 22, "Physics", 3.6)
 
# Auf einzelne Elemente zugreifen (nullbasiert)
name = student[0]
age = student[1]
major = student[2]
gpa = student[3]
 
print(f"{name} is {age} years old")  # Output: Bob is 22 years old
print(f"Major: {major}, GPA: {gpa}")  # Output: Major: Physics, GPA: 3.6
 
# Negatives Indexing funktioniert auch
last_item = student[-1]
print(f"Last item: {last_item}")  # Output: Last item: 3.6
 
# Slicing extrahiert ein neues Tupel
first_two = student[:2]
print(first_two)  # Output: ('Bob', 22)
print(type(first_two))  # Output: <class 'tuple'>

Jede Indexierungs- und Slicing-Technik, die Sie mit Listen in Kapitel 14 gelernt haben, funktioniert mit Tupeln identisch. Der entscheidende Unterschied ist, dass Tupel nach dem Erstellen nicht verändert werden können.

Mehrere Elemente

Ein Element

Leer

Erstellen eines Tupels

Syntaxwahl

(item1, item2, ...)

(item,) - Komma erforderlich

()

15.2) Tupel-Packing und -Unpacking

Eine der leistungsfähigsten und elegantesten Eigenschaften von Tupeln ist ihre Fähigkeit, mehrere Werte zusammenzupacken und sie in separate Variablen zu entpacken. Diese Funktion macht Python-Code bemerkenswert kurz und gut lesbar.

Tupel-Packing

Tupel-Packing (tuple packing) geschieht, wenn Sie ein Tupel erstellen, indem Sie mehrere Werte, getrennt durch Kommas, zusammen angeben:

python
# Werte in ein Tupel packen
coordinates = 10, 20, 30
print(coordinates)  # Output: (10, 20, 30)
 
# Verschiedene Typen packen
user_data = "Alice", 25, "alice@example.com"
print(user_data)  # Output: ('Alice', 25, 'alice@example.com')
 
# Rückgabewerte einer Funktion packen
def get_statistics(numbers):
    total = sum(numbers)
    count = len(numbers)
    average = total / count
    return total, count, average  # Packt drei Werte in ein Tupel
 
stats = get_statistics([85, 90, 78, 92, 88])
print(stats)  # Output: (433, 5, 86.6)

Wenn eine Funktion mehrere durch Kommas getrennte Werte zurückgibt, packt Python sie automatisch in ein Tupel. Deshalb können Funktionen so wirken, als würden sie mehrere Werte zurückgeben—tatsächlich geben sie ein einzelnes Tupel zurück, das diese Werte enthält.

Tupel-Unpacking

Tupel-Unpacking (tuple unpacking) ist der umgekehrte Vorgang: Werte aus einem Tupel in separate Variablen zu extrahieren:

python
# Grundlegendes Unpacking
point = (100, 200)
x, y = point
print(f"x = {x}, y = {y}")  # Output: x = 100, y = 200
 
# Unpacking funktioniert mit jeder Sequenz, nicht nur mit Tupeln
name, age, email = ["Bob", 30, "bob@example.com"]
print(f"{name} is {age} years old")  # Output: Bob is 30 years old
 
# Rückgabewerte einer Funktion direkt entpacken
total, count, average = get_statistics([95, 88, 92, 85])
print(f"Average of {count} scores: {average}")  # Output: Average of 4 scores: 90.0

Die Anzahl der Variablen auf der linken Seite muss zur Anzahl der Elemente in der Sequenz passen. Wenn sie nicht übereinstimmt, wirft Python eine ValueError:

python
# Das verursacht einen Fehler
coordinates = (10, 20, 30)
# x, y = coordinates  # ValueError: too many values to unpack (expected 2)
 
# Das verursacht ebenfalls einen Fehler
point = (5, 10)
# x, y, z = point  # ValueError: not enough values to unpack (expected 3, got 2)

Variablen mit Tupel-Unpacking tauschen

Tupel-Unpacking ermöglicht eine elegante Methode, Variablenwerte zu tauschen, ohne eine temporäre Variable zu benötigen:

python
# Traditioneller Tausch mit einer temporären Variable
a = 10
b = 20
temp = a
a = b
b = temp
print(f"a = {a}, b = {b}")  # Output: a = 20, b = 10
 
# Pythons eleganter Tausch mit Tupel-Unpacking
x = 100
y = 200
x, y = y, x  # Tausch in einer Zeile!
print(f"x = {x}, y = {y}")  # Output: x = 200, y = 100
 
# Mehr als zwei Variablen tauschen
first = "A"
second = "B"
third = "C"
first, second, third = third, first, second
print(first, second, third)  # Output: C A B

Wie funktioniert das? Python wertet zuerst die rechte Seite aus, erstellt dabei ein Tupel (y, x) und entpackt es dann in die Variablen auf der linken Seite. Das passiert in einem einzigen Schritt, daher ist keine temporäre Variable nötig.

Erweitertes Unpacking mit dem Stern-Operator

Python bietet erweitertes Unpacking (extended unpacking) mit dem *-Operator, um mehrere Elemente einzusammeln:

python
# Unpacking mit einer „Rest“-Variable
scores = (95, 88, 92, 85, 90, 87)
first, second, *rest = scores
print(f"Top two: {first}, {second}")  # Output: Top two: 95, 88
print(f"Others: {rest}")  # Output: Others: [92, 85, 90, 87]
print(type(rest))  # Output: <class 'list'>
 
# Der Stern kann überall stehen
numbers = (1, 2, 3, 4, 5)
first, *middle, last = numbers
print(f"First: {first}")  # Output: First: 1
print(f"Middle: {middle}")  # Output: Middle: [2, 3, 4]
print(f"Last: {last}")  # Output: Last: 5
 
# Den Anfang einfangen
*beginning, second_last, last = numbers
print(f"Beginning: {beginning}")  # Output: Beginning: [1, 2, 3]
print(f"Last two: {second_last}, {last}")  # Output: Last two: 4, 5

Beachten Sie, dass die mit Stern markierte Variable die Elemente immer als Liste (list) einfängt, selbst wenn aus einem Tupel entpackt wird. Wenn es keine Elemente zum Einfangen gibt, wird die Stern-Variable eine leere Liste:

python
# Wenn es nichts einzufangen gibt
a, b, *rest = (10, 20)
print(rest)  # Output: []
 
# Pro Unpacking ist nur ein Stern erlaubt
# first, *middle, *end = (1, 2, 3, 4)  # SyntaxError: multiple starred expressions

Werte mit Unterstrich ignorieren

Manchmal benötigen Sie aus einem Tupel nur bestimmte Werte. Konventionsgemäß verwenden Python-Programmierer den Unterstrich _ als Variablennamen, um Werte zu kennzeichnen, die sie ignorieren möchten:

python
# Einen Datums-String parsen
date_string = "2024-03-15"
year, month, day = date_string.split("-")
print(f"Month: {month}")  # Output: Month: 03
 
# Wenn uns nur der Monat interessiert
_, month, _ = date_string.split("-")
print(f"Month: {month}")  # Output: Month: 03
 
# Mit erweitertem Unpacking
data = ("Alice", 25, "Engineer", "New York", "alice@example.com")
name, age, *_, email = data
print(f"{name} ({age}): {email}")  # Output: Alice (25): alice@example.com

Der Unterstrich ist nur ein ganz normaler Variablenname, aber seine Verwendung signalisiert anderen Programmierern (und Ihnen selbst), dass Sie diese Werte absichtlich ignorieren.

Praktische Beispiele für Packing und Unpacking

python
# Mehrere Werte aus Berechnungen zurückgeben
def calculate_rectangle_properties(width, height):
    """Berechnen Sie Fläche und Umfang eines Rechtecks."""
    area = width * height
    perimeter = 2 * (width + height)
    return area, perimeter  # Packing
 
# Die Ergebnisse entpacken
rect_area, rect_perimeter = calculate_rectangle_properties(5, 3)
print(f"Area: {rect_area}, Perimeter: {rect_perimeter}")  # Output: Area: 15, Perimeter: 16
 
# Iteration mit Unpacking
students = [
    ("Alice", 85),
    ("Bob", 92),
    ("Carol", 78)
]
 
for name, score in students:  # Unpacking in der Schleife
    print(f"{name}: {score}")
# Output:
# Alice: 85
# Bob: 92
# Carol: 78

Tupel-Packing und -Unpacking machen Python-Code besser lesbar und ausdrucksstärker. Statt auf Tupel-Elemente per Index (student[0], student[1]) zuzugreifen, können Sie sie in sinnvoll benannte Variablen entpacken.

15.3) Tupel sind unveränderlich: Wann das nützlich ist

Das definierende Merkmal von Tupeln ist ihre Unveränderlichkeit (immutability)—einmal erstellt, können die Inhalte eines Tupels nicht geändert werden. Sie können keine Elemente hinzufügen, entfernen oder verändern. Diese Unveränderlichkeit kann wie eine Einschränkung wirken, bietet aber wichtige Vorteile.

Was Unveränderlichkeit in der Praxis bedeutet

python
# Ein Tupel erstellen
coordinates = (10, 20, 30)
print(coordinates)  # Output: (10, 20, 30)
 
# Ein Änderungsversuch führt zu einem Fehler
# coordinates[0] = 15  # TypeError: 'tuple' object does not support item assignment
 
# Ein Versuch, Elemente hinzuzufügen, führt zu einem Fehler
# coordinates.append(40)  # AttributeError: 'tuple' object has no attribute 'append'
 
# Ein Versuch, Elemente zu entfernen, führt zu einem Fehler
# del coordinates[1]  # TypeError: 'tuple' object doesn't support item deletion

Wenn Python sagt, dass Tupel keine Zuweisung zu Elementen unterstützen, bedeutet das, dass Sie nicht ändern können, was an einer Position im Tupel gespeichert ist. Die Struktur des Tupels ist beim Erstellen festgelegt.

Vergleich von veränderbaren Listen und unveränderlichen Tupeln

python
# Listen sind veränderbar – Sie können sie ändern
shopping_list = ["milk", "bread", "eggs"]
shopping_list[1] = "butter"  # Ein Element ändern
shopping_list.append("cheese")  # Ein Element hinzufügen
print(shopping_list)  # Output: ['milk', 'butter', 'eggs', 'cheese']
 
# Tupel sind unveränderlich – Sie können sie nicht ändern
product_dimensions = (10, 20, 5)  # width, height, depth in cm
# product_dimensions[0] = 12  # TypeError: cannot modify
# product_dimensions.append(3)  # AttributeError: no append method
 
# Um ein Tupel zu „ändern“, müssen Sie ein neues erstellen
new_dimensions = (12, 20, 5)  # Ein komplett neues Tupel erstellen
print(new_dimensions)  # Output: (12, 20, 5)

Warum Unveränderlichkeit nützlich ist

Unveränderlichkeit bietet mehrere praktische Vorteile:

1. Datenintegrität und Sicherheit

Wenn Sie ein Tupel an eine Funktion übergeben, wissen Sie, dass die Funktion Ihre Daten nicht versehentlich verändern kann:

python
def calculate_distance(point1, point2):
    """Berechnen Sie die Distanz zwischen zwei 2D-Punkten."""
    x1, y1 = point1
    x2, y2 = point2
 
    dx = x2 - x1
    dy = y2 - y1
    
    # Selbst wenn wir wollten, können wir die Eingabe-Tupel nicht verändern
 
    return (dx**2 + dy**2) ** 0.5
 
start = (0, 0)
end = (3, 4)
distance = calculate_distance(start, end)
print(f"Distance: {distance}")  # Output: Distance: 5.0
print(f"Start point unchanged: {start}")  # Output: Start point unchanged: (0, 0)

Bei Listen müssten Sie sich Gedanken darüber machen, ob eine Funktion Ihre Daten verändern könnte. Bei Tupeln haben Sie die Garantie, dass das nicht passiert.

2. Tupel als Dictionary-Schlüssel verwenden

Wie wir in Kapitel 17 näher untersuchen werden, müssen Dictionary-Schlüssel hashbar (hashable) sein—sie müssen einen Hash-Wert haben, der sich niemals ändert. Unveränderliche Objekte wie Tupel können Dictionary-Schlüssel sein; veränderliche Objekte wie Listen nicht:

python
# Tupel können Dictionary-Schlüssel sein
locations = {
    (0, 0): "Origin",
    (10, 20): "Point A",
    (30, 40): "Point B"
}
print(locations[(10, 20)])  # Output: Point A
 
# Listen können keine Dictionary-Schlüssel sein
# locations_bad = {
#     [0, 0]: "Origin"  # TypeError: unhashable type: 'list'
# }

3. Absicht signalisieren

Die Verwendung eines Tupels statt einer Liste kommuniziert anderen Programmierern (und Ihnen selbst), dass sich diese Daten nicht ändern sollen:

python
# RGB-Farbwerte – diese sollten sich niemals ändern
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
 
# Datenbank-Verbindungsparameter – feste Konfiguration
DB_CONFIG = ("localhost", 5432, "myapp", "production")
 
# Geografische Koordinaten – ein Ort ändert sich nicht
EIFFEL_TOWER = (48.8584, 2.2945)  # latitude, longitude

Wenn Sie ein Tupel im Code sehen, wissen Sie sofort, dass diese Daten konstant bleiben sollen. Wenn Sie eine Liste sehen, wissen Sie, dass sie möglicherweise verändert wird.

4. Performance-Vorteile

Weil Tupel unveränderlich sind, kann Python sie auf Arten optimieren, die bei Listen nicht möglich sind. Wir lernen das sys-Modul in Kapitel 27 kennen, aber fürs Erste genügt: sys.getsizeof() sagt uns, wie viel Speicher ein Objekt verwendet:

python
import sys
 
# Tupel benötigen weniger Speicher als äquivalente Listen (list-Objekte)
tuple_data = (1, 2, 3, 4, 5)
list_data = [1, 2, 3, 4, 5]
 
print(f"Tuple size: {sys.getsizeof(tuple_data)} bytes")  # Output: Tuple size: 80 bytes (may vary by Python version)
print(f"List size: {sys.getsizeof(list_data)} bytes")    # Output: List size: 104 bytes (may vary by Python version)
 
# Das Erstellen von Tupeln ist schneller
import timeit
 
tuple_time = timeit.timeit("(1, 2, 3, 4, 5)", number=1000000)
list_time = timeit.timeit("[1, 2, 3, 4, 5]", number=1000000)
 
print(f"Tuple creation: {tuple_time:.4f} seconds")
print(f"List creation: {list_time:.4f} seconds")
# Example output: Tuple creation: 0.0055 seconds, List creation: 0.0292 seconds

15.4) Die Unveränderlichkeits-Falle: Wenn Tupel veränderliche Elemente enthalten

Obwohl Tupel selbst unveränderlich sind, können sie veränderliche Objekte wie Listen oder Dictionaries enthalten. Das erzeugt eine subtile, aber wichtige Unterscheidung: Die Struktur des Tupels ist fest, aber die Inhalte veränderlicher Objekte darin können sich weiterhin ändern.

Die Unterscheidung verstehen

python
# Ein Tupel, das eine Liste enthält
student_data = ("Alice", 20, [85, 90, 78])  # name, age, scores
print(student_data)  # Output: ('Alice', 20, [85, 90, 78])
 
# Wir können Tupel-Elemente nicht neu zuweisen
# student_data[0] = "Bob"  # TypeError: 'tuple' object does not support item assignment
 
# Aber wir KÖNNEN die Liste innerhalb des Tupels verändern
student_data[2].append(92)  # Einen neuen Score hinzufügen
print(student_data)  # Output: ('Alice', 20, [85, 90, 78, 92])
 
student_data[2][0] = 88  # Einen bestehenden Score ändern
print(student_data)  # Output: ('Alice', 20, [88, 90, 78, 92])

Was passiert hier? Das Tupel speichert drei Referenzen: eine auf den String "Alice", eine auf die Ganzzahl 20 und eine auf ein Listenobjekt. Die Struktur des Tupels—welche Objekte es referenziert—kann sich nicht ändern. Das Listenobjekt selbst ist jedoch veränderlich, daher können sich dessen Inhalte ändern.

Den Unterschied visualisieren

python
# Die Tupel-Struktur ist fest
data = ("Python", [1, 2, 3])
 
# Das versucht zu ändern, worauf das Tupel referenziert – NICHT ERLAUBT
# data[1] = [4, 5, 6]  # TypeError
 
# Das verändert die Liste, auf die das Tupel referenziert – ERLAUBT
data[1].append(4)
print(data)  # Output: ('Python', [1, 2, 3, 4])
 
# Das Tupel referenziert weiterhin dasselbe Listenobjekt
# Nur der Inhalt der Liste hat sich geändert, nicht welche Liste das Tupel meint

Stellen Sie es sich so vor: Ein Tupel ist wie eine Reihe von Kästchen, und jedes Kästchen enthält eine Referenz auf ein Objekt. Die Kästchen selbst sind fest verriegelt (unveränderlich), aber wenn ein Kästchen eine Referenz auf ein veränderliches Objekt enthält, kann sich dieses Objekt trotzdem ändern.

Tupel mit Dictionaries

Dasselbe Prinzip gilt für Dictionaries in Tupeln:

python
# Tupel, das ein Dictionary enthält
user_profile = ("alice", {"email": "alice@example.com", "age": 25})
print(user_profile)  # Output: ('alice', {'email': 'alice@example.com', 'age': 25})
 
# Kann nicht ändern, auf welches Dictionary das Tupel referenziert
# user_profile[1] = {"email": "newemail@example.com"}  # TypeError
 
# Kann aber das Dictionary selbst ändern
user_profile[1]["age"] = 26
user_profile[1]["city"] = "New York"
print(user_profile)  # Output: ('alice', {'email': 'alice@example.com', 'age': 26, 'city': 'New York'})

Warum das für Dictionary-Schlüssel wichtig ist

Tupel können nur dann als Dictionary-Schlüssel verwendet werden, wenn all ihre Elemente hashbar sind. Obwohl Tupel selbst unveränderlich sind, ist ein Tupel, das veränderliche Objekte (wie Listen) enthält, überhaupt nicht hashbar und kann daher nicht als Dictionary-Schlüssel verwendet werden.

python
# Das funktioniert, ist aber gefährlich
tuple_with_list = ("key", [1, 2, 3])
# data = {tuple_with_list: "value"}  # TypeError: unhashable type: 'list'

Verwenden Sie als Dictionary-Schlüssel nur Tupel, die vollständig unveränderliche Objekte (Strings, Zahlen, frozensets, andere Tupel) enthalten.

Wirklich unveränderliche Tupel erstellen

Wenn Sie ein Tupel benötigen, das komplett unveränderlich ist, stellen Sie sicher, dass auch alle Inhalte unveränderlich sind:

python
# Vollständig unveränderliches Tupel – nur unveränderliche Typen
point_3d = (10, 20, 30)  # All integers
rgb_color = (255, 128, 0)  # All integers
coordinates = ((10, 20), (30, 40))  # Tuple of tuples
 
# Diese sind sicher als Dictionary-Schlüssel zu verwenden
color_names = {
    (255, 0, 0): "Red",
    (0, 255, 0): "Green",
    (0, 0, 255): "Blue"
}
 
# Verschachtelte Tupel bleiben unveränderlich
nested = ((1, 2), (3, 4))
# nested[0][0] = 5  # TypeError: 'tuple' object does not support item assignment

Wenn veränderliche Inhalte beabsichtigt sind

Manchmal möchten Sie tatsächlich ein Tupel mit veränderlichen Inhalten—zum Beispiel, wenn Sie eine feste Datensatzstruktur haben, aber ein Feld sich ändern soll:

python
# Schülerdatensatz mit fester Identität, aber veränderlichen Noten
def create_student(name, student_id):
    """Erstellen Sie einen Schülerdatensatz mit leerer Notenliste."""
    return (name, student_id, [])  # name und ID fest, grades können sich ändern
 
student = create_student("Alice", "S12345")
print(student)  # Output: ('Alice', 'S12345', [])
 
# Die Identität des Schülers ist fest
print(f"Student: {student[0]} (ID: {student[1]})")  # Output: Student: Alice (ID: S12345)
 
# Aber wir können Noten hinzufügen, sobald sie entstehen
student[2].append(85)
student[2].append(92)
student[2].append(78)
print(f"Grades: {student[2]}")  # Output: Grades: [85, 92, 78]
 
# Die Tupel-Struktur schützt name und ID vor versehentlichen Änderungen
# während die Notenliste wachsen darf

Dieses Muster ist nützlich, wenn Sie einige Daten schützen möchten, während andere Daten sich ändern dürfen. Seien Sie sich nur der Unterscheidung zwischen der Unveränderlichkeit des Tupels und der Veränderlichkeit seiner Inhalte bewusst.

15.5) Wann man Tupel statt Listen verwendet

Die Wahl zwischen Tupeln und Listen ist eine wichtige Designentscheidung. Obwohl beide Sequenzen sind, erfüllen sie unterschiedliche Zwecke und kommunizieren unterschiedliche Absichten.

Verwenden Sie Tupel für feste, heterogene Daten

Tupel funktionieren am besten, wenn Sie eine feste Anzahl an Elementen haben, die eine einzelne logische Einheit darstellen, oft mit unterschiedlichen Typen:

python
# Schülerdatensatz: name, age, major, GPA
student = ("Alice", 20, "Computer Science", 3.8)
 
# Geografische Koordinaten: latitude, longitude
location = (40.7128, -74.0060)  # New York City
 
# RGB-Farbe: red, green, blue
color = (255, 128, 0)
 
# Datenbankverbindung: host, port, database, username
db_connection = ("localhost", 5432, "myapp", "admin")
 
# Datum: year, month, day
date = (2024, 3, 15)

Jedes Tupel stellt einen vollständigen „Datensatz“ dar, bei dem die Position jedes Elements eine spezifische Bedeutung hat. Das erste Element ist immer der Name, das zweite immer das Alter und so weiter.

Verwenden Sie Listen für homogene Sammlungen

Listen funktionieren am besten, wenn Sie eine variable Anzahl ähnlicher Elemente haben, die Sie hinzufügen, entfernen oder neu anordnen könnten:

python
# Einkaufsliste – Elemente des gleichen Typs (Strings)
shopping_list = ["milk", "bread", "eggs", "butter"]
shopping_list.append("cheese")  # Bei Bedarf weitere Elemente hinzufügen
shopping_list.remove("bread")   # Elemente entfernen
 
# Testergebnisse – Elemente des gleichen Typs (Zahlen)
test_scores = [85, 92, 78, 95, 88]
test_scores.append(90)  # Neues Ergebnis hinzufügen
test_scores.sort()      # Ergebnisse neu anordnen
 
# Benutzernamen – Elemente des gleichen Typs (Strings)
active_users = ["alice", "bob", "carol"]
active_users.extend(["dave", "eve"])  # Mehrere Benutzer hinzufügen

Listen sind für Sammlungen gedacht, bei denen sich die Anzahl der Elemente ändern kann und bei denen jedes Element dieselbe Rolle spielt.

Tupel für Funktionsrückgabewerte

Wenn eine Funktion mehrere zusammengehörige Werte zurückgibt, sind Tupel die natürliche Wahl:

python
def get_user_info(user_id):
    """Benutzerinformationen aus der Datenbank abrufen."""
    # Datenbankabfrage simulieren
    return "Alice", "alice@example.com", 25, "New York"
 
# Das zurückgegebene Tupel entpacken
name, email, age, city = get_user_info(101)
print(f"{name} from {city}")  # Output: Alice from New York
 
def calculate_statistics(numbers):
    """Minimum, Maximum und Durchschnitt von Zahlen berechnen."""
    if not numbers:
        return None, None, None
    
    minimum = min(numbers)
    maximum = max(numbers)
    average = sum(numbers) / len(numbers)
    return minimum, maximum, average
 
# Die Ergebnisse entpacken
min_val, max_val, avg_val = calculate_statistics([85, 92, 78, 95, 88])
print(f"Range: {min_val} to {max_val}, Average: {avg_val}")
# Output: Range: 78 to 95, Average: 87.6

Das Zurückgeben von Tupeln macht deutlich, dass diese Werte zusammengehören und gemeinsam betrachtet werden sollten.

Tupel für Dictionary-Schlüssel

Wenn Sie zusammengesetzte Schlüssel in einem Dictionary benötigen, sind Tupel unverzichtbar:

python
# Noten nach Kurs und Semester
grades = {
    ("CS101", "Fall2023"): 85,
    ("CS101", "Spring2024"): 90,
    ("MATH201", "Fall2023"): 88,
    ("MATH201", "Spring2024"): 92
}
 
# Eine bestimmte Note nachschlagen
course = "CS101"
semester = "Spring2024"
grade = grades[(course, semester)]
print(f"Grade in {course} ({semester}): {grade}")  # Output: Grade in CS101 (Spring2024): 90
 
# Gitter-Koordinaten als Dictionary-Schlüssel
grid = {
    (0, 0): "Start",
    (5, 3): "Obstacle",
    (10, 10): "Goal"
}
 
position = (5, 3)
if position in grid:
    print(f"At {position}: {grid[position]}")  # Output: At (5, 3): Obstacle

Listen können keine Dictionary-Schlüssel sein, weil sie veränderlich sind, aber Tupel können es.

Tupel für unveränderliche Konfiguration

Wenn Sie Konfigurationsdaten haben, die sich niemals ändern sollen, signalisieren Tupel diese Absicht:

python
# Anwendungseinstellungen, die konstant bleiben sollen
APP_CONFIG = (
    "MyApp",           # Application name
    "1.0.0",          # Version
    "production",     # Environment
    True,             # Debug mode
    8080              # Port
)
 
# Farbpalette für UI – diese Farben sind fest
COLOR_PALETTE = (
    (255, 0, 0),      # Primary red
    (0, 128, 255),    # Primary blue
    (255, 255, 255),  # White
    (0, 0, 0)         # Black
)
 
# API-Endpunkte – diese URLs ändern sich nicht
API_ENDPOINTS = (
    "https://api.example.com/users",
    "https://api.example.com/products",
    "https://api.example.com/orders"
)

Entscheidungshilfe

python
# Verwenden Sie TUPEL, wenn:
# 1. Daten einen einzelnen Datensatz mit fester Struktur darstellen
employee = ("E001", "Alice", "Engineering", 75000)
 
# 2. Mehrere Werte von einer Funktion zurückgegeben werden
def divide_with_remainder(a, b):
    return a // b, a % b
 
# 3. Als Dictionary-Schlüssel benötigt
cache = {(5, 10): 50, (3, 7): 21}
 
# 4. Daten nicht verändert werden sollen
SCREEN_RESOLUTION = (1920, 1080)
 
# Verwenden Sie LISTEN, wenn:
# 1. Sammlung ähnlicher Elemente sich ändern kann
tasks = ["Write code", "Test code", "Deploy code"]
tasks.append("Document code")
 
# 2. Elemente hinzugefügt, entfernt oder neu angeordnet werden müssen
scores = [85, 90, 78]
scores.sort()
scores.append(92)
 
# 3. Alle Elemente denselben Zweck erfüllen
usernames = ["alice", "bob", "carol"]
 
# 4. Die Größe der Sammlung im Voraus nicht bekannt ist
results = []
for i in range(10):
    results.append(i * 2)

15.6) range-Objekte im Detail verstehen

Jetzt, da wir verstanden haben, wann man Tupel gegenüber Listen verwendet, schauen wir uns Pythons dritten unveränderlichen Sequenztyp an: range-Objekte. Der Typ range stellt eine unveränderliche Sequenz von Zahlen dar. Anders als Listen und Tupel, die alle ihre Elemente im Speicher ablegen, erzeugen range-Objekte Zahlen bei Bedarf, was sie extrem speichereffizient macht, um große Zahlenfolgen darzustellen.

range-Objekte erstellen

Die Funktion range() erstellt range-Objekte in drei Formen:

python
# Ein Argument: range(stop)
# Erzeugt Zahlen von 0 bis (aber ohne) stop
numbers = range(5)
print(list(numbers))  # Output: [0, 1, 2, 3, 4]
 
# Zwei Argumente: range(start, stop)
# Erzeugt Zahlen von start bis (aber ohne) stop
numbers = range(2, 7)
print(list(numbers))  # Output: [2, 3, 4, 5, 6]
 
# Drei Argumente: range(start, stop, step)
# Erzeugt Zahlen von start bis stop und erhöht um step
numbers = range(0, 10, 2)
print(list(numbers))  # Output: [0, 2, 4, 6, 8]
 
# Negativer step zum Herunterzählen
numbers = range(10, 0, -1)
print(list(numbers))  # Output: [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

Beachten Sie, dass wir range-Objekte mit list() in Listen umwandeln, um ihren Inhalt zu sehen. Ein range-Objekt selbst zeigt beim Ausgeben nicht alle Werte an:

python
r = range(5)
print(r)  # Output: range(0, 5)
print(type(r))  # Output: <class 'range'>

Wie range-Objekte funktionieren

range-Objekte speichern nicht all ihre Werte im Speicher. Stattdessen berechnen sie jeden Wert, wenn er benötigt wird:

python
import sys
 
# Ein range, das eine Million Zahlen repräsentiert
large_range = range(1000000)
print(f"Range size: {sys.getsizeof(large_range)} bytes")  # Output: Range size: 48 bytes (may vary by Python version)
 
# Eine Liste, die eine Million Zahlen enthält
large_list = list(range(1000000))
print(f"List size: {sys.getsizeof(large_list)} bytes")  # Output: List size: 8000056 bytes (approximately 8MB)
 
# Der range ist winzig; die Liste ist riesig!

Ein range-Objekt speichert nur drei Werte: start, stop und step. Es berechnet jede Zahl in der Sequenz, wenn Sie sie anfordern. Das macht range-Objekte unglaublich effizient für große Sequenzen.

range in for-Schleifen verwenden

Wie wir in Kapitel 12 gelernt haben, werden range-Objekte am häufigsten mit for-Schleifen verwendet:

python
# Zählen von 0 bis 4
for i in range(5):
    print(f"Count: {i}")
# Output:
# Count: 0
# Count: 1
# Count: 2
# Count: 3
# Count: 4
 
# Zählen von 1 bis 10
for i in range(1, 11):
    print(i, end=" ")
print()  # Output: 1 2 3 4 5 6 7 8 9 10
 
# In Zweierschritten zählen
for i in range(0, 20, 2):
    print(i, end=" ")
print()  # Output: 0 2 4 6 8 10 12 14 16 18
 
# Rückwärts zählen
for i in range(5, 0, -1):
    print(f"T-minus {i}")
# Output:
# T-minus 5
# T-minus 4
# T-minus 3
# T-minus 2
# T-minus 1

Indexing und Slicing bei range-Objekten

range-Objekte unterstützen Indexing und Slicing genauso wie andere Sequenzen:

python
# Einen range erstellen
numbers = range(10, 50, 5)  # 10, 15, 20, 25, 30, 35, 40, 45
 
# Indexing
print(numbers[0])   # Output: 10
print(numbers[3])   # Output: 25
print(numbers[-1])  # Output: 45
 
# Slicing gibt einen neuen range zurück
subset = numbers[2:5]
print(subset)  # Output: range(20, 35, 5)
print(list(subset))  # Output: [20, 25, 30]
 
# Länge
print(len(numbers))  # Output: 8

Zugehörigkeit prüfen

Sie können mit dem Operator in prüfen, ob eine Zahl in einem range liegt:

python
# Gerade Zahlen von 0 bis 20
evens = range(0, 21, 2)
 
print(10 in evens)  # Output: True
print(15 in evens)  # Output: False
print(20 in evens)  # Output: True
 
# Das ist sehr effizient – Python erzeugt nicht alle Zahlen
# Es berechnet, ob die Zahl in der Sequenz liegen würde
large_range = range(0, 1000000, 3)
print(999999 in large_range)  # Output: True (instant, no iteration needed)

Python kann die Zugehörigkeit mathematisch bestimmen, ohne alle Zahlen zu erzeugen, was diese Operation selbst bei riesigen range-Objekten extrem schnell macht.

Leere und umgekehrte Ranges

python
# Leerer range – stop entspricht start
empty = range(5, 5)
print(list(empty))  # Output: []
print(len(empty))   # Output: 0
 
# Leerer range – stop ist mit gegebenem step nicht erreichbar
impossible = range(1, 10, -1)  # Kann mit negativem step nicht hochzählen
print(list(impossible))  # Output: []
 
# Umgekehrter range
backwards = range(10, 0, -1)
print(list(backwards))  # Output: [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
 
# Umgekehrt mit negativen Zahlen
negative_range = range(-5, -15, -2)
print(list(negative_range))  # Output: [-5, -7, -9, -11, -13]

Wann range statt Listen verwenden

python
# Verwenden Sie range, wenn:
# 1. Sie eine Zahlenfolge für Iteration brauchen
for i in range(100):
    # Etwas 100-mal verarbeiten
    pass
 
# 2. Sie Indizes für eine Sequenz brauchen
items = ["a", "b", "c", "d"]
for i in range(len(items)):
    print(f"Index {i}: {items[i]}")
 
# 3. Speichereffizienz bei großen Sequenzen wichtig ist
# Das nutzt minimalen Speicher
for i in range(1000000):
    if i % 100000 == 0:
        print(i)
 
# Verwenden Sie Listen, wenn:
# 1. Sie die tatsächlichen Werte speichern müssen
squares = [1, 3, 5, 7, 10]
 
# 2. Sie die Sequenz verändern müssen
numbers = list(range(5))
numbers[2] = 100  # Einen Wert ändern
numbers.append(200)  # Einen Wert hinzufügen
 
# 3. Sie die Sequenz mehrfach mit unterschiedlichen Operationen verwenden müssen
data = list(range(10))
print(sum(data))
print(max(data))
print(sorted(data, reverse=True))

range-Objekte sind ein perfektes Beispiel für Pythons Effizienz. Sie bieten alle Vorteile einer Sequenz ohne die Speicherkosten, jedes Element abzulegen.

15.7) Zwischen Listen, Tupeln und Ranges konvertieren

Python macht es einfach, zwischen verschiedenen Sequenztypen zu konvertieren. Wenn Sie diese Konvertierungen verstehen, können Sie für jede Situation den richtigen Typ wählen und Daten bei Bedarf transformieren.

In Listen konvertieren

Die Funktion list() konvertiert jede Sequenz in eine Liste:

python
# Tupel in Liste
student_tuple = ("Alice", 20, "CS")
student_list = list(student_tuple)
print(student_list)  # Output: ['Alice', 20, 'CS']
print(type(student_list))  # Output: <class 'list'>
 
# Jetzt können wir sie verändern
student_list[1] = 21
student_list.append(3.8)
print(student_list)  # Output: ['Alice', 21, 'CS', 3.8]
 
# range in Liste
numbers = range(5)
numbers_list = list(numbers)
print(numbers_list)  # Output: [0, 1, 2, 3, 4]
 
# String in Liste (jedes Zeichen wird ein Element)
text = "Python"
chars = list(text)
print(chars)  # Output: ['P', 'y', 't', 'h', 'o', 'n']

Die Konvertierung in eine Liste ist nützlich, wenn Sie eine Sequenz verändern müssen oder wenn Sie listenspezifische Methoden wie append(), sort() oder remove() benötigen.

In Tupel konvertieren

Die Funktion tuple() konvertiert jede Sequenz in ein Tupel:

python
# Liste in Tupel
scores_list = [85, 90, 78, 92]
scores_tuple = tuple(scores_list)
print(scores_tuple)  # Output: (85, 90, 78, 92)
print(type(scores_tuple))  # Output: <class 'tuple'>
 
# Jetzt ist es unveränderlich
# scores_tuple[0] = 88  # TypeError: 'tuple' object does not support item assignment
 
# range in Tupel
numbers = range(1, 6)
numbers_tuple = tuple(numbers)
print(numbers_tuple)  # Output: (1, 2, 3, 4, 5)
 
# String in Tupel
text = "Hi"
chars_tuple = tuple(text)
print(chars_tuple)  # Output: ('H', 'i')

Die Konvertierung in ein Tupel ist nützlich, wenn Sie Daten vor Änderungen schützen möchten oder wenn Sie eine Sequenz als Dictionary-Schlüssel verwenden müssen.

list

tuple

tuple

list

Range

Liste

Tupel

15.8) Häufige Sequenz-Operationen über Strings, Listen, Tupel und Ranges hinweg

Pythons Sequenztypen—Strings, Listen, Tupel und range-Objekte—teilen viele gemeinsame Operationen. Wenn Sie diese gemeinsamen Operationen verstehen, können Sie effizient mit jedem Sequenztyp arbeiten.

Länge, Minimum und Maximum

Alle Sequenzen unterstützen die Funktionen len(), min() und max():

python
# Strings
text = "Python"
print(len(text))  # Output: 6
print(min(text))  # Output: P (smallest character by Unicode value)
print(max(text))  # Output: y (largest character by Unicode value)
 
# Listen
numbers = [45, 12, 78, 23, 56]
print(len(numbers))  # Output: 5
print(min(numbers))  # Output: 12
print(max(numbers))  # Output: 78
 
# Tupel
scores = (85, 92, 78, 95, 88)
print(len(scores))  # Output: 5
print(min(scores))  # Output: 78
print(max(scores))  # Output: 95
 
# Ranges
nums = range(10, 50, 5)
print(len(nums))  # Output: 8
print(min(nums))  # Output: 10
print(max(nums))  # Output: 45

Damit min() und max() funktionieren, müssen die Elemente vergleichbar sein. Sie können nicht das Minimum einer Liste finden, die sowohl Strings als auch Zahlen enthält:

python
mixed = [1, "hello", 3]
# print(min(mixed))  # TypeError: '<' not supported between instances of 'str' and 'int'

Indexing und negatives Indexing

Alle Sequenzen unterstützen Indexing mit positiven und negativen Indizes:

python
# Positives Indexing (0-basiert)
text = "Python"
numbers = [10, 20, 30, 40, 50]
coords = (5, 10, 15)
values = range(0, 100, 10)
 
print(text[0])      # Output: P
print(numbers[2])   # Output: 30
print(coords[1])    # Output: 10
print(values[3])    # Output: 30
 
# Negatives Indexing (vom Ende)
print(text[-1])     # Output: n (last character)
print(numbers[-2])  # Output: 40 (second from end)
print(coords[-3])   # Output: 5 (third from end, which is first)
print(values[-1])   # Output: 90 (last value in range)

Negative Indizes zählen vom Ende: -1 ist das letzte Element, -2 ist das vorletzte und so weiter.

Zugehörigkeitstest mit in und not in

Alle Sequenzen unterstützen Zugehörigkeitstests:

python
# Strings – prüft auf Teilstrings
text = "Python Programming"
print("Python" in text)      # Output: True
print("Java" in text)        # Output: False
print("gram" in text)        # Output: True (substring)
print("PYTHON" not in text)  # Output: True (case-sensitive)
 
# Listen
fruits = ["apple", "banana", "cherry", "date"]
print("banana" in fruits)    # Output: True
print("grape" in fruits)     # Output: False
print("apple" not in fruits) # Output: False
 
# Tupel
coordinates = (10, 20, 30, 40)
print(20 in coordinates)     # Output: True
print(25 in coordinates)     # Output: False
print(50 not in coordinates) # Output: True
 
# Ranges – sehr effizient, keine Iteration nötig
numbers = range(0, 100, 2)  # Even numbers 0 to 98
print(50 in numbers)         # Output: True
print(51 in numbers)         # Output: False (odd number)
print(100 in numbers)        # Output: False (stop is exclusive)

Bei Ranges kann Python die Zugehörigkeit mathematisch bestimmen, ohne jedes Element zu prüfen, was selbst bei riesigen range-Objekten extrem schnell ist.

Verkettung und Wiederholung

Strings, Listen und Tupel unterstützen Verkettung mit + und Wiederholung mit *:

python
# Verkettung mit +
text1 = "Hello"
text2 = " World"
print(text1 + text2)  # Output: Hello World
 
list1 = [1, 2, 3]
list2 = [4, 5, 6]
print(list1 + list2)  # Output: [1, 2, 3, 4, 5, 6]
 
tuple1 = (10, 20)
tuple2 = (30, 40)
print(tuple1 + tuple2)  # Output: (10, 20, 30, 40)
 
# Wiederholung mit *
print("Ha" * 3)           # Output: HaHaHa
print([0] * 5)            # Output: [0, 0, 0, 0, 0]
print((1, 2) * 3)         # Output: (1, 2, 1, 2, 1, 2)

Wichtig: Ranges unterstützen weder Verkettung noch Wiederholung:

python
r1 = range(5)
r2 = range(5, 10)
# combined = r1 + r2  # TypeError: unsupported operand type(s) for +: 'range' and 'range'
 
# Um Ranges zu kombinieren, zuerst in Listen oder Tupel konvertieren
combined = list(r1) + list(r2)
print(combined)  # Output: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Vorkommen zählen

Die Methode count() gibt zurück, wie oft ein Element vorkommt:

python
# Strings – zählt Teilstring-Vorkommen
text = "Mississippi"
print(text.count("s"))   # Output: 4
print(text.count("ss"))  # Output: 2
print(text.count("i"))   # Output: 4
 
# Listen
numbers = [1, 2, 3, 2, 4, 2, 5]
print(numbers.count(2))  # Output: 3
print(numbers.count(6))  # Output: 0
 
# Tupel
grades = (85, 90, 85, 92, 85, 88)
print(grades.count(85))  # Output: 3
print(grades.count(95))  # Output: 0
 
# Ranges haben keine count()-Methode, aber Sie können zuerst konvertieren
nums = range(0, 20, 2)
nums_list = list(nums)
print(nums_list.count(10))  # Output: 1

Index von Elementen finden

Die Methode index() gibt die Position des ersten Vorkommens zurück:

python
# Strings
text = "Python Programming"
print(text.index("P"))      # Output: 0 (first P)
print(text.index("Pro"))    # Output: 7 (substring position)
# print(text.index("Java"))  # ValueError: substring not found
 
# Listen
fruits = ["apple", "banana", "cherry", "banana"]
print(fruits.index("banana"))  # Output: 1 (first occurrence)
print(fruits.index("cherry"))  # Output: 2
# print(fruits.index("grape"))  # ValueError: 'grape' is not in list
 
# Tupel
coordinates = (10, 20, 30, 20, 40)
print(coordinates.index(20))  # Output: 1 (first occurrence)
print(coordinates.index(40))  # Output: 4
 
# Ranges haben keine index()-Methode, aber Sie können zuerst konvertieren
nums = range(10, 50, 5)
nums_list = list(nums)
print(nums_list.index(25))  # Output: 3

Wenn das Element nicht gefunden wird, wirft index() eine ValueError. Um das zu vermeiden, prüfen Sie zuerst mit in:

python
fruits = ["apple", "banana", "cherry"]
search_fruit = "grape"
 
if search_fruit in fruits:
    position = fruits.index(search_fruit)
    print(f"{search_fruit} found at position {position}")
else:
    print(f"{search_fruit} not found")
# Output: grape not found

Iteration mit for-Schleifen

Alle Sequenzen können mit for-Schleifen iteriert werden:

python
# Strings – über Zeichen iterieren
for char in "Python":
    print(char, end=" ")
print()  # Output: P y t h o n
 
# Listen
for fruit in ["apple", "banana", "cherry"]:
    print(f"I like {fruit}")
# Output:
# I like apple
# I like banana
# I like cherry
 
# Tupel
for score in (85, 90, 78):
    print(f"Score: {score}")
# Output:
# Score: 85
# Score: 90
# Score: 78
 
# Ranges
for i in range(1, 6):
    print(f"Count: {i}")
# Output:
# Count: 1
# Count: 2
# Count: 3
# Count: 4
# Count: 5

Vergleichsoperationen

Sequenzen können mit ==, !=, <, >, <= und >= verglichen werden:

python
# Gleichheit
print([1, 2, 3] == [1, 2, 3])      # Output: True
print((1, 2, 3) == (1, 2, 3))      # Output: True
print("abc" == "abc")               # Output: True
 
# Ungleichheit
print([1, 2, 3] != [1, 2, 4])      # Output: True
print((1, 2) != (1, 2))            # Output: False
 
# Lexikografischer Vergleich (Element für Element)
print([1, 2, 3] < [1, 2, 4])       # Output: True (3 < 4)
print([1, 2, 3] < [1, 3, 0])       # Output: True (2 < 3)
print("apple" < "banana")           # Output: True (alphabetical)
print((1, 2) < (1, 2, 3))          # Output: True (shorter is less if equal so far)
 
# Unterschiedliche Typen vergleichen
print([1, 2, 3] == (1, 2, 3))      # Output: False (different types)

Der Vergleich funktioniert von links nach rechts Element für Element. Das erste abweichende Element bestimmt das Ergebnis.

Wenn Sie diese gemeinsamen Operationen verstehen, können Sie Code schreiben, der mit jedem Sequenztyp funktioniert, wodurch Ihre Programme flexibler und wiederverwendbarer werden.

15.9) Fortgeschrittenes Slicing über alle Sequenztypen hinweg

Slicing ist eine der leistungsfähigsten Funktionen in Python, um mit Sequenzen zu arbeiten. Während wir grundlegendes Slicing in Kapitel 14 eingeführt haben, gibt es fortgeschrittene Slicing-Techniken, die über alle Sequenztypen hinweg funktionieren.

Wiederholung: Basis-Slicing

Slicing extrahiert einen Teil einer Sequenz mit der Syntax sequence[start:stop:step]:

python
# Basis-Slicing mit Strings
text = "Python Programming"
print(text[0:6])    # Output: Python
print(text[7:18])   # Output: Programming
print(text[7:])     # Output: Programming (from index 7 to end)
print(text[:6])     # Output: Python (from start to index 6)
 
# Basis-Slicing mit Listen
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(numbers[2:7])   # Output: [2, 3, 4, 5, 6]
print(numbers[:5])    # Output: [0, 1, 2, 3, 4]
print(numbers[5:])    # Output: [5, 6, 7, 8, 9]
 
# Basis-Slicing mit Tupeln
coordinates = (10, 20, 30, 40, 50, 60)
print(coordinates[1:4])  # Output: (20, 30, 40)
print(coordinates[:3])   # Output: (10, 20, 30)
print(coordinates[3:])   # Output: (40, 50, 60)
 
# Basis-Slicing mit Ranges
nums = range(0, 100, 10)
print(list(nums[2:5]))   # Output: [20, 30, 40]

Denken Sie daran: start ist inklusive, stop ist exklusiv, und das Ergebnis ist immer vom gleichen Typ wie die ursprüngliche Sequenz.

Step beim Slicing verwenden

Der optionale dritte Parameter step steuert, wie viele Elemente übersprungen werden:

python
# Jedes zweite Element
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(numbers[::2])     # Output: [0, 2, 4, 6, 8]
print(numbers[1::2])    # Output: [1, 3, 5, 7, 9]
 
# Jedes dritte Element
text = "abcdefghijklmnop"
print(text[::3])        # Output: adgjmp
 
# Step mit start und stop
print(numbers[2:8:2])   # Output: [2, 4, 6]
print(text[1:10:2])     # Output: bdfhj

Negativer Step: Sequenzen umkehren

Ein negativer Step kehrt die Richtung des Slicings um:

python
# Ganze Sequenzen umkehren
text = "Python"
print(text[::-1])       # Output: nohtyP
 
numbers = [1, 2, 3, 4, 5]
print(numbers[::-1])    # Output: [5, 4, 3, 2, 1]
 
coordinates = (10, 20, 30, 40)
print(coordinates[::-1])  # Output: (40, 30, 20, 10)
 
# Umkehren mit step
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(numbers[::-2])    # Output: [9, 7, 5, 3, 1] (every second, backwards)
 
# Einen Teil umkehren
text = "Python Programming"
print(text[7:18][::-1])  # Output: gnimmargorP (reverse "Programming")

Bei negativem Step funktionieren start und stop anders:

python
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
 
# Bei negativem Step sollte start größer als stop sein
print(numbers[7:2:-1])   # Output: [7, 6, 5, 4, 3] (from 7 down to 3)
print(numbers[8:3:-2])   # Output: [8, 6, 4] (from 8 down to 4, step -2)
 
# start/stop bei negativem Step weglassen
print(numbers[:5:-1])    # Output: [9, 8, 7, 6] (from end down to 6)
print(numbers[5::-1])    # Output: [5, 4, 3, 2, 1, 0] (from 5 down to start)

Negative Indizes beim Slicing

Sie können negative Indizes für start- und stop-Positionen verwenden:

python
text = "Python Programming"
# Letzte 11 Zeichen
print(text[-11:])        # Output: Programming
 
# Alles außer den letzten 11 Zeichen
print(text[:-11])        # Output: Python
 
# Von -15 bis -5
print(text[-15:-5])      # Output: hon Progra
 
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# Letzte 5 Elemente
print(numbers[-5:])      # Output: [5, 6, 7, 8, 9]
 
# Alles außer den letzten 3 Elementen
print(numbers[:-3])      # Output: [0, 1, 2, 3, 4, 5, 6]
 
# Von -7 bis -2
print(numbers[-7:-2])    # Output: [3, 4, 5, 6, 7]

Slicing mit Ranges

Slicing eines range gibt ein neues range-Objekt zurück:

python
# Ranges slicen
numbers = range(0, 100, 5)  # 0, 5, 10, 15, ..., 95
print(numbers)  # Output: range(0, 100, 5)
 
# Slice gibt einen neuen range zurück
subset = numbers[5:10]
print(subset)  # Output: range(25, 50, 5)
print(list(subset))  # Output: [25, 30, 35, 40, 45]
 
# Mit step
every_other = numbers[::2]
print(list(every_other))  # Output: [0, 10, 20, 30, 40, 50, 60, 70, 80, 90]
 
# Negativer step
reversed_range = numbers[::-1]
print(list(reversed_range))  # Output: [95, 90, 85, ..., 5, 0]

Leere Slices und Randfälle

python
numbers = [1, 2, 3, 4, 5]
 
# Leere Slices (start >= stop bei positivem step)
print(numbers[3:3])    # Output: []
print(numbers[5:10])   # Output: [] (stop beyond length)
print(numbers[10:20])  # Output: [] (both beyond length)
 
# Slices außerhalb der Grenzen sind sicher
print(numbers[-100:100])  # Output: [1, 2, 3, 4, 5] (entire sequence)
print(numbers[2:100])     # Output: [3, 4, 5] (from 2 to end)
 
# Negativer step mit inkompatiblem start/stop
print(numbers[2:7:-1])    # Output: [] (can't go forward with negative step)
 
# step von 0 ist nicht erlaubt
# print(numbers[::0])  # ValueError: slice step cannot be zero

Slicing zum Kopieren

Slicing erstellt eine neue Sequenz, was eine Möglichkeit zum Kopieren bietet:

python
# Kopieren mit Slicing
original = [1, 2, 3, 4, 5]
copy = original[:]  # Slice von Anfang bis Ende
print(copy)  # Output: [1, 2, 3, 4, 5]
 
# Änderungen an der Kopie beeinflussen das Original nicht
copy[0] = 100
print(f"Original: {original}")  # Output: Original: [1, 2, 3, 4, 5]
print(f"Copy: {copy}")          # Output: Copy: [100, 2, 3, 4, 5]
 
# Das funktioniert auch für Tupel (erstellt ein neues Tupel)
original_tuple = (1, 2, 3, 4, 5)
copy_tuple = original_tuple[:]
print(copy_tuple)  # Output: (1, 2, 3, 4, 5)
 
# Für Strings
text = "Python"
text_copy = text[:]
print(text_copy)  # Output: Python

Denken Sie jedoch daran aus Kapitel 14, dass dies eine flache Kopie (shallow copy) erzeugt.

python
# Einschränkung der flachen Kopie
original = [[1, 2], [3, 4]]
copy = original[:]
 
# Das Verändern einer verschachtelten Liste wirkt sich auf beide aus
copy[0][0] = 100
print(f"Original: {original}")  # Output: Original: [[100, 2], [3, 4]]
print(f"Copy: {copy}")          # Output: Copy: [[100, 2], [3, 4]]

Tupel und range-Objekte sind essenzielle Werkzeuge in Pythons Sequenz-Werkzeugkasten. Tupel liefern unveränderliche, strukturierte Daten, die Informationen vor versehentlichen Änderungen schützen und ihre Verwendung als Dictionary-Schlüssel ermöglichen. range-Objekte bieten speichereffiziente Repräsentationen von Zahlenfolgen—perfekt für Schleifen und große Sequenzen. Wenn Sie verstehen, wann Sie welchen Typ verwenden—und wie Sie zwischen ihnen konvertieren—wird Ihr Code effizienter, sicherer und in seiner Absicht klarer.

Die gemeinsamen Operationen, die alle Sequenztypen teilen—Indexing, Slicing, Iteration, Zugehörigkeitstests—bilden eine konsistente Schnittstelle, die die Arbeit mit jeder Sequenz intuitiv macht. Fortgeschrittene Slicing-Techniken geben Ihnen leistungsfähige, ausdrucksstarke Möglichkeiten, Sequenzdaten zu extrahieren und zu manipulieren.

Wenn Sie in Python weiter programmieren, werden Sie ganz natürlich den richtigen Sequenztyp für jede Situation wählen: Listen für Sammlungen, die sich ändern, Tupel für feste Datensätze, range-Objekte für Zahlenfolgen und Strings für Text. Dieses Kapitel hat Ihnen das Wissen gegeben, diese Entscheidungen sicher zu treffen und jeden Typ effektiv zu nutzen.

© 2025. Primesoft Co., Ltd.
support@primesoft.ai