Listen und Veränderlichkeit
In Python sind Listen Datenstrukturen, die nach ihrer Erstellung geändert werden können. Eine Mutation verändert die vorhandene Liste. Eine Zuweisung bindet einen Namen.
Wird eine Liste einer Variablen zugewiesen, verweist die Variable auf dieses Listenobjekt. Die Zuweisung kopiert die Liste nicht. Eine weitere Variable oder ein Container kann daher auf genau dasselbe Objekt verweisen.
# Liste erstellen und Referenz bereitstellen
list1 = [0, 1, 2]
list2 = ['a', list1, True]
print(list2) # ['a', [0, 1, 2], True]
# Elemente in Liste1 ändern
list1[1] = [3, 4, 5]
print(list2) # ['a', [0, [3, 4, 5], 2], True]Änderungen an list1 sind auch über list2 sichtbar. list2 ist eine andere, äußere Liste. Ihr Element list2[1] verweist auf dasselbe Objekt wie list1.
Ein vollständiger Slice erzeugt eine flache Kopie
Ein vollständiger Slice erzeugt eine neue äußere Liste mit Verweisen auf dieselben Elemente. Das ist eine flache Kopie. Wird ein äußeres Element ersetzt, bleibt die ursprüngliche Liste unverändert. Ein verschachteltes veränderliches Element wird jedoch weiterhin gemeinsam genutzt.
# Slice-Operationen erstellen neue Listen
list1 = [0, 1, 2]
list2 = list1[:] # Alle Elemente mit Slice nehmen
print(id(list1)) # Beispiel: 3104
print(id(list2)) # Beispiel: 8864 (anders als list1)
# Das Ändern von Elementen in Liste2 hat keine Auswirkungen auf Liste1
list2[0] = 'x'
print(list1) # [0, 1, 2] (unverändert)
print(list2) # ['x', 1, 2]
# Verschachtelte veränderliche Elemente bleiben gemeinsam genutzt
nested1 = [[1], [2]]
nested2 = nested1[:]
nested2[0].append(3)
print(nested1) # [[1, 3], [2]]Identität Typ und Wert eines Objekts
Jedes Python-Objekt besitzt drei Merkmale: Identität, Typ und Wert.
Objektidentität
Zwei Verweise haben dieselbe Identität, wenn sie auf dasselbe Objekt zeigen. id() liefert eine Ganzzahl, die unter gleichzeitig lebenden Objekten eindeutig ist. is vergleicht die Identität direkt. Die Sprache garantiert nicht, dass diese Ganzzahl eine physische Speicheradresse ist.
a = 300
print(a) # 300
print(type(a)) # <class 'int'>
print(id(a)) # Beispiel: 10120
b = a # b verweist auf dasselbe Objekt wie a
print(id(b)) # Beispiel: 10120 (wie a)
a = a + 1 # Erstellt neues Objekt
print(id(a)) # Beispiel: 10492 (anders als zuvor)Identität und Wert vergleichen
Verwende is, wenn es um die Identität eines Objekts geht, besonders bei Prüfungen wie value is None. Verwende ==, wenn Werte verglichen werden sollen. Interpreter-Caches und Constant Folding können dazu führen, dass gleiche unveränderliche Werte dieselbe Identität haben. An dieses Implementierungsdetail darf Anwendungslogik jedoch nicht gekoppelt sein.
a = [1, 2]
b = a
c = [1, 2]
print(a is b) # True: dasselbe Objekt
print(a == b) # True: derselbe Wert
print(a is c) # False: verschiedene Objekte
print(a == c) # True: gleiche WerteVeränderliche und unveränderliche Objekte
Python teilt seine Objekte nach einer einzigen Frage ein, nämlich ob sich der Inhalt nach der Erstellung noch ändern lässt. Ein unveränderliches Objekt wie ein int, ein str oder ein tuple behält sein Leben lang denselben Wert, eine Operation, die wie eine Änderung aussieht, erzeugt deshalb ein neues Objekt. Ein veränderliches Objekt wie eine list, ein dict oder ein set trägt einen Inhalt, der sich ändern kann, während das Objekt dasselbe bleibt.
Dieser Unterschied wirkt, sobald zwei Namen auf dasselbe Objekt zeigen, denn bei einem veränderlichen Objekt sieht jeder Name die Änderung.
Unveränderliche Objekte
Unveränderliche Objekte können nach der Erstellung nicht mehr geändert werden. Jede Operation, die scheinbar das Objekt verändert, erstellt tatsächlich ein neues Objekt.
# Ganzzahl ist unveränderlich
a = 300
print(id(a)) # Beispiel: 10120
a = a + 1 # Erstellt neues Objekt
print(id(a)) # Beispiel: 10492 (anders)
# String ist auch unveränderlich
str_var = 'hello'
print(id(str_var)) # Beispiel: 75568
# Der Versuch, die Zeichenfolge zu ändern, führt zu einem Fehler
# str_var[1] = 'a' # TypeErrorZu den häufigen unveränderlichen Typen in Python gehören:
bool(boolesch)int(Ganzzahl)float(Gleitkommazahl)complex(komplexe Zahl)str(String)tuple(Tupel)
Die eigene Folge von Verweisen eines Tupels kann nicht verändert werden. Ein Objekt, auf das das Tupel verweist, kann dennoch veränderlich sein.
Veränderliche Objekte
Veränderbare Objekte können nach der Erstellung geändert werden, ohne dass ein neues Objekt erstellt werden muss. Die Identität des Objekts bleibt gleich, auch wenn sich sein Wert ändert.
# Liste ist veränderbar
list_var = [0, 1, 2]
original = list_var
original_id = id(original)
# Das Ändern eines Elements erhält die Identität der Liste
list_var[0] = 9
print(id(list_var) == original_id) # True
# Eine Zuweisung bindet die Variable an ein neues Objekt
list_var = [0, 0, 0]
print(id(list_var) == original_id) # False
# Eine Slice-Zuweisung verändert die aktuelle Liste direkt
current_id = id(list_var)
list_var[:] = ['a', 'b', 'c']
print(id(list_var) == current_id) # TrueZu den häufigen veränderlichen Typen in Python gehören:
list(Liste)dict(Dictionary)set(Menge)
Zuweisung und Änderungsverhalten
Eine Zuweisung bindet einen Namen an ein Objekt. Eine direkte Änderung verändert das vorhandene Objekt.
list1 = [0, 1, 2]
list2 = list1 # Beide Variablen verweisen auf dasselbe Objekt
print(id(list1)) # Beispiel: 700
print(id(list2)) # Beispiel: 700 (gleich)
# Die direkte Änderung wirkt sich auf beide Variablen aus
list1[1] = 'x'
print(list1) # [0, 'x', 2]
print(list2) # [0, 'x', 2] (auch geändert)
# Zuweisung erstellt neue Referenz
list1 = [1, 2, 3]
print(list1) # [1, 2, 3]
print(list2) # [0, 'x', 2] (unverändert)Mutabilität bestimmt, ob sich ein vorhandenes Objekt ändern lässt. Eine Zuweisung kann einen Namen unabhängig davon neu binden. Unveränderlichkeit beseitigt eine Quelle von Fehlern bei gemeinsamem Zustand, macht ein Programm allein aber noch nicht threadsicher. Verwiesene veränderliche Objekte, zusammengesetzte Operationen und externe Ressourcen benötigen weiterhin eine bewusst gewählte Synchronisierung.