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:
# 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: 0Tupel 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.
# 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:
# 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:
# 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:
# 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.
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:
# 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:
# 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.0Die Anzahl der Variablen auf der linken Seite muss zur Anzahl der Elemente in der Sequenz passen. Wenn sie nicht übereinstimmt, wirft Python eine ValueError:
# 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:
# 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 BWie 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:
# 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, 5Beachten 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:
# 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 expressionsWerte 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:
# 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.comDer 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
# 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: 78Tupel-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
# 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 deletionWenn 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
# 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:
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:
# 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:
# 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, longitudeWenn 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:
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 seconds15.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
# 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
# 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 meintStellen 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:
# 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.
# 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:
# 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 assignmentWenn 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:
# 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 darfDieses 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:
# 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:
# 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ügenListen 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:
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.6Das 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:
# 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): ObstacleListen 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:
# 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
# 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:
# 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:
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:
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:
# 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 1Indexing und Slicing bei range-Objekten
range-Objekte unterstützen Indexing und Slicing genauso wie andere Sequenzen:
# 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: 8Zugehörigkeit prüfen
Sie können mit dem Operator in prüfen, ob eine Zahl in einem range liegt:
# 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
# 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
# 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:
# 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:
# 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.
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():
# 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: 45Damit 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:
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:
# 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:
# 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 *:
# 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:
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:
# 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: 1Index von Elementen finden
Die Methode index() gibt die Position des ersten Vorkommens zurück:
# 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: 3Wenn das Element nicht gefunden wird, wirft index() eine ValueError. Um das zu vermeiden, prüfen Sie zuerst mit in:
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 foundIteration mit for-Schleifen
Alle Sequenzen können mit for-Schleifen iteriert werden:
# 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: 5Vergleichsoperationen
Sequenzen können mit ==, !=, <, >, <= und >= verglichen werden:
# 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]:
# 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:
# 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: bdfhjNegativer Step: Sequenzen umkehren
Ein negativer Step kehrt die Richtung des Slicings um:
# 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:
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:
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:
# 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
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 zeroSlicing zum Kopieren
Slicing erstellt eine neue Sequenz, was eine Möglichkeit zum Kopieren bietet:
# 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: PythonDenken Sie jedoch daran aus Kapitel 14, dass dies eine flache Kopie (shallow copy) erzeugt.
# 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.