In Python a variable is a name, not a box. Assignment doesn't copy a value into a slot — it ties a name to an object that lives somewhere in memory. Two names can point at the same object, and whether changing one is visible through the other depends on two things: is the object mutable, and do you change it in place or rebind the name to something new?
Where you meet it
- Passing a list or dict into a function — and finding it changed afterwards.
b = afollowed by surprise that editingaalso editedb.- The infamous mutable default argument that "remembers" between calls.
- Comparing objects with
iswhen you meant==(or the reverse).
What it does
Every object has three fixed things: an identity (its address, never changes), a type, and a value. For mutable objects (lists, dicts, sets) the value can change in place; for immutable ones (int, str, tuple) it can't. Names just refer to these objects — assignment moves the name, it never copies the object.
b = a never copies — it sticks a second label on the same object. Whether that bites you depends on one thing: can the object be mutated?
How it works
Think of a name as a label stuck on an object. b = a sticks a second label on the same object — that's an alias, and a is b is True. Mutating through one label is visible through every label. Rebinding a name (=) just peels its label off and moves it to a different object; the others stay put.
a = [1, 2, 3]
b = a # b is an alias — same object
a.append(99) # mutate in place
print(b) # [1, 2, 3, 99] ← b sees it too!
x = 5
y = x # both name the int 5
x = x + 1 # int is immutable → builds a NEW object
print(y) # 5 ← y still names the old 5
The list is mutated, so both names see the change. The int can't be mutated, so x + 1 creates a fresh object and rebinds x only — y is untouched. To get an independent list, copy it: b = a.copy() or b = a[:].
Watch out
- Mutable default arguments.
def f(items=[])creates the list once, when the function is defined, and shares it across every call. Useitems=Noneand build a fresh list inside. isis not==.iscompares identity (same object);==compares value. Useisonly for singletons likeNone; use==for everything else.- A shallow copy isn't deep.
list.copy(),[:]andcopy.copy()duplicate the outer object but keep references to nested objects. For full independence usecopy.deepcopy().
Go deeper
In Python ist eine Variable ein Name, keine Kiste. Eine Zuweisung kopiert keinen Wert in ein Fach — sie bindet einen Namen an ein Objekt, das irgendwo im Speicher liegt. Zwei Namen können auf dasselbe Objekt zeigen, und ob eine Änderung am einen durch den anderen sichtbar ist, hängt an zwei Fragen: Ist das Objekt veränderlich (mutable), und änderst du es an Ort und Stelle oder bindest du den Namen an etwas Neues?
Wo es vorkommt
- Eine Liste oder ein Dict an eine Funktion übergeben — und es danach verändert vorfinden.
b = aund dann die Überraschung, dass das Ändern vonaauchbgeändert hat.- Das berüchtigte veränderliche Default-Argument, das sich zwischen Aufrufen „merkt".
- Objekte mit
isvergleichen, wenn==gemeint war (oder umgekehrt).
Was es tut
Jedes Objekt hat drei feste Dinge: eine Identität (seine Adresse, ändert sich nie), einen Typ und einen Wert. Bei veränderlichen Objekten (Listen, Dicts, Sets) kann der Wert in-place geändert werden; bei unveränderlichen (int, str, tuple) nicht. Namen verweisen nur auf diese Objekte — eine Zuweisung verschiebt den Namen, sie kopiert nie das Objekt.
b = a kopiert nie — es klebt ein zweites Etikett aufs selbe Objekt. Ob dich das beißt, hängt an einem: Lässt sich das Objekt mutieren?
Wie es funktioniert
Stell dir einen Namen als Etikett auf einem Objekt vor. b = a klebt ein zweites Etikett auf dasselbe Objekt — das ist ein Alias, und a is b ergibt True. Eine In-place-Änderung über ein Etikett ist über alle Etiketten sichtbar. Einen Namen neu zuzuweisen (=) löst nur sein Etikett ab und klebt es auf ein anderes Objekt; die übrigen bleiben, wo sie sind.
a = [1, 2, 3]
b = a # b ist ein Alias — dasselbe Objekt
a.append(99) # in-place mutieren
print(b) # [1, 2, 3, 99] ← b sieht es auch!
x = 5
y = x # beide benennen die int 5
x = x + 1 # int ist unveränderlich → baut ein NEUES Objekt
print(y) # 5 ← y benennt weiter die alte 5
Die Liste wird mutiert, also sehen beide Namen die Änderung. Die int lässt sich nicht mutieren, also erzeugt x + 1 ein frisches Objekt und bindet nur x neu — y bleibt unberührt. Für eine unabhängige Liste kopieren: b = a.copy() oder b = a[:].
Worauf achten
- Veränderliche Default-Argumente.
def f(items=[])erzeugt die Liste einmal, bei der Definition der Funktion, und teilt sie über alle Aufrufe. Stattdessenitems=Nonenehmen und drinnen eine frische Liste bauen. isist nicht==.isvergleicht die Identität (dasselbe Objekt),==den Wert.isnur für Singletons wieNoneverwenden,==für alles andere.- Eine flache Kopie ist nicht tief.
list.copy(),[:]undcopy.copy()duplizieren das äußere Objekt, behalten aber Referenzen auf verschachtelte Objekte. Für volle Unabhängigkeitcopy.deepcopy()nutzen.
Mehr dazu
- Python-Doku — Objects, values and types (Identität, Typ, Wert; mutable vs immutable)
- Python-Doku — das
copy-Modul (flache vs tiefe Kopie) - Python-FAQ — warum Default-Werte zwischen Objekten geteilt werden
- Python-FAQ — wann man sich auf
isstatt==verlassen kann - Ned Batchelder — Facts and myths about Python names and values