R

ReflexKR

Reflex-Dokumentation

Der Etiketten-Flughafen: Pythons Objektmodell

·

Kursüberblick

1

Das Etikett ist nicht der Koffer: Namen und Objekte

2

Versiegelte Koffer: veränderlich und unveränderlich

3

Koffer im Koffer: Container und Kopien

4

Wenn ein Koffer übergeben wird: Funktionen und Argumente

5

Zonen und Ausweise: Gültigkeitsbereiche

6

Handgepäck: Closures

7

Der Koffer-Bauplan: Klassen und Instanzen

8

Flughafen-Standardverfahren: Dunder-Methoden

9

Der Bauplan hinter dem Bauplan: Vererbung und MRO

10

Die Kontrolle: das Geheimnis des Attributzugriffs

11

Herrenlose Koffer: Speicher und GC

12

Schließfachnummern: Hash, Gleichheit und Gesamtbild

Der Etiketten-Flughafen: Pythons Objektmodell
Lektion 3

Zeit 1:40–2:30

Koffer im Koffer: Container und Kopien

Verlustmeldung

LOST-003

Wasser in Klasse 1, nasse Koffer in Klasse 2

Gemeldet von

Yoon, Lehrkraft und Begleitung einer Klassenfahrt (Mittelschule)

Was fehlt

Die Ausrüstungssets für die Klassen. Das Grundset wurde „dupliziert“ und an Klasse 1 und Klasse 2 verteilt. Als Wasser in einen Koffer von Klasse 1 lief, wurden auch die Handtücher im Koffer von Klasse 2 nass.

Erledigt, wenn

Klären, wie weit die Sets beider Klassen derselbe Koffer waren, und Sets packen, die bis ganz nach innen getrennt sind.

Internationaler Flughafen Haneulseom · Gepäckzentrum

Zehn Uhr vormittags: Die Begleitlehrkraft kommt mit einem Foto nasser Handtücher ins Zentrum. Die Sets seien eindeutig mit copy() dupliziert worden, und trotzdem seien die Handtücher beider Klassen nass geworden.

Schichtleiterin Do Miran dreht ein Set um. Es gibt tatsächlich zwei äußere Koffer. Doch in den äußeren Koffern stecken keine Gegenstände, sondern Etiketten. „Hier wurden nur neue Außenkoffer gekauft. Die Etiketten darin zeigen auf dieselbe Handtuchtasche.“

3-1. Im Koffer stecken Etiketten (10 min)

Container wie Listen, Tupel, dict und set enthalten nicht die Objekte selbst. Sie enthalten Referenzen auf Objekte, also Etiketten. So wie in Lektion 1 ein Name ein Etikett war, ist auch jedes Fach einer Liste ein Etikett.

Deshalb kann dasselbe Objekt gleichzeitig in mehreren Fächern stecken.

row = [0, 0]
pair = [row, row]
row.append(7)
print(pair)
# [[0, 0, 7], [0, 0, 7]]
print(pair[0] is pair[1])
# True

Am häufigsten richtet diese Eigenschaft bei zweidimensionalen Listen Schaden an.

grid = [[0] * 3] * 3
grid[0][0] = 9
print(grid)
# [[9, 0, 0], [9, 0, 0], [9, 0, 0]]
print(grid[0] is grid[1] is grid[2])
# True

[0] * 3 erzeugt eine Liste. Das äußere * 3 kopiert diese Liste nicht dreimal, sondern steckt dreimal ein Etikett auf dieselbe Liste hinein. Ein Koffer, drei Etiketten.

Das innere [0] * 3 ist dagegen unproblematisch. Zwar zeigt jedes Fach auf dieselbe Ganzzahl 0, aber Ganzzahlen sind unveränderlich, und row[0] = 9 hängt nur das Etikett in diesem Fach auf die 9 um. Zum Problem wird das Teilen erst, wenn ein veränderliches Objekt geteilt wird.

Die Lösung ist eine Comprehension, die für jede Zeile eine neue Liste erzeugt:

grid = [[0] * 3 for _ in range(3)]
grid[0][0] = 9
print(grid)
# [[9, 0, 0], [0, 0, 0], [0, 0, 0]]

Etikettentafel

Schritt 1 / 0

Als Nächstes läuft: 0

Labor wird vorbereitet…

Gerade geändert

Versiegelter Koffer (unveränderlich)

Bei [[0] * 3] * 3 laufen die Pfeile aus allen drei Fächern der äußeren Liste in einem Koffer zusammen. Die Comprehension packt für jede Zeile einen eigenen neuen Koffer.

3-2. Flache Kopie: nur der äußere Koffer ist neu (10 min)

Für das Kopieren einer Liste gibt es fünf gängige Wege. Alle liefern dasselbe Ergebnis.

import copy
kit = [["water", "map"], ["snack"]]
copies = [kit[:], list(kit), kit.copy(), copy.copy(kit)]
print([c is kit for c in copies])
# [False, False, False, False]
print([c[0] is kit[0] for c in copies])
# [True, True, True, True]

Das ist eine flache Kopie (shallow copy). Der äußere Koffer wird neu angelegt, die Etiketten darin werden einfach übernommen. Deshalb macht es einen Unterschied, ob du den äußeren oder einen inneren Koffer veränderst.

a = kit[:]
a.append(["hat"])          # Fach im äußeren Koffer hinzufügen: Original bleibt gleich
print(len(kit), len(a))
# 2 3
a[0].append("towel")       # inneren Koffer ändern: auch im Original sichtbar
print(kit[0])
# ['water', 'map', 'towel']

Bei dict ist es genauso. d.copy(), dict(d) und {**d} sind allesamt flache Kopien.

rooms = {"class1": ["water"], "class2": ["map"]}
dup = rooms.copy()
dup["class3"] = []               # neuer Schlüssel: nicht im Original
dup["class1"].append("tea")      # geteilte Liste: auch im Original sichtbar
print(rooms)
# {'class1': ['water', 'tea'], 'class2': ['map']}

Probier unten =, flache Kopie und tiefe Kopie an derselben verschachtelten Liste aus. Zeile 4 verändert einen inneren Koffer, Zeile 5 fügt dem äußeren Koffer ein Fach hinzu.

Etikettentafel

Schritt 1 / 0

Als Nächstes läuft: 0

Labor wird vorbereitet…

Gerade geändert

Versiegelter Koffer (unveränderlich)

Bei = ist auch der äußere Koffer derselbe, also sieht man Zeile 4 und 5 in kit. Bei kit[:] ist nur der äußere Koffer neu, also sieht man nur Zeile 4. copy.deepcopy legt auch die inneren Listen neu an, kit bleibt unverändert.

Pin sagt

→ Eine flache Kopie legt nur „eine Ebene“ neu an. Stecken nur Zahlen oder Strings darin, reicht sie völlig. Stecken Listen oder dicts darin, überleg dir, ob du eine tiefe Kopie brauchst.

3-3. Tiefe Kopie: alles bis ganz nach innen neu (9 min)

copy.deepcopy steigt in den Koffer im Koffer und in den Koffer darin hinab und legt alles neu an.

import copy
kit = [["water", "map"], ["snack"]]
deep = copy.deepcopy(kit)
deep[0].append("rain")
print(kit[0], deep[0], deep[0] is kit[0])
# ['water', 'map'] ['water', 'map', 'rain'] False

Eine Kopie, die bis ganz nach unten geht, hat zwei Fallen: Man trifft denselben Koffer zweimal, oder ein Koffer enthält sich selbst, und die Kopie nähme kein Ende. deepcopy löst beides mit einem Buch namens memo. Es notiert jedes bereits kopierte Objekt als {id(Original): Kopie} und verwendet beim nächsten Treffen die notierte Kopie.

x = [1]
pair = [x, x]
p2 = copy.deepcopy(pair)
print(p2[0] is p2[1], p2[0] is x)
# True False

memo = {}
copy.deepcopy(pair, memo)
print(id(x) in memo)
# True

Zwei Fächer, die im Original auf denselben Koffer zeigten, zeigen auch in der Kopie auf denselben Koffer. Sogar die Beziehungen des Teilens werden mitkopiert.

Auch eine Liste, die sich selbst enthält, führt nicht in eine Endlosschleife. Gibt man eine solche Liste aus, kürzt Python sie mit [...] ab.

a = []
a.append(a)
print(a)
# [[...]]
b = copy.deepcopy(a)
print(b[0] is b, b is a)
# True False

Etikettentafel

Schritt 1 / 0

Als Nächstes läuft: 0

Labor wird vorbereitet…

Gerade geändert

Versiegelter Koffer (unveränderlich)

Nach Zeile 3 zeigt das Fach der Liste auf die Liste selbst. Die Kopie von deepcopy ist eine neue Liste, die ebenfalls auf sich selbst zeigt. Mit dem Original ist sie nicht verbunden.
  • Eigene Klassen können mit den Methoden __copy__ und __deepcopy__ selbst festlegen, wie sie kopiert werden. Es genügt zu wissen, dass es das gibt.
  • Eine tiefe Kopie durchläuft den gesamten Objektgraphen, ist also langsam und braucht viel Speicher. Oft ist es besser, nur die Ebenen neu anzulegen, die man wirklich braucht.

3-4. Versiegelte Koffer werden nicht kopiert (6 min)

Unveränderliche Objekte ändern sich nie, also gibt es keinen Grund, eine Kopie anzulegen. copy gibt einfach das Original zurück.

import copy
t = (1, 2)
print(copy.copy(t) is t, copy.deepcopy(t) is t)
# True True
s = "gate"
print(copy.deepcopy(s) is s)
# True

Anders sieht es aus, wenn im Tupel ein veränderliches Objekt steckt. Die flache Kopie gibt weiterhin das Original zurück. Die tiefe Kopie muss aber die Liste darin neu anlegen und erzeugt deshalb ein neues Tupel.

t2 = (1, [2])
print(copy.copy(t2) is t2)
# True
dt = copy.deepcopy(t2)
print(dt is t2, dt == t2, dt[1] is t2[1])
# False True False

Dasselbe gilt für die Strings in einer tief kopierten Liste. Die Liste ist neu, aber "map" darin ist dasselbe String-Objekt wie im Original.

kit = [["map"], ["snack"]]
deep = copy.deepcopy(kit)
print(deep[0] is kit[0], deep[0][0] is kit[0][0])
# False True

Auch eine tiefe Kopie packt versiegelte Koffer nicht neu. Was sich nicht ändern kann, darf man gefahrlos teilen.

3-5. copy.replace(): ein neues Objekt mit einzelnen Änderungen (5 min)

Will man ein unveränderliches Objekt „ändern“, erzeugt man ein neues, in dem nur einzelne Teile anders sind. Seit 3.13 gibt es dafür einen einheitlichen Weg: copy.replace(). Unterstützt wird es von Dataclasses mit frozen=True, von namedtuple, von datetime.date und ähnlichen Objekten.

import copy
from dataclasses import dataclass
from collections import namedtuple

@dataclass(frozen=True)
class Ticket:
    gate: str
    seat: str

tk = Ticket("A1", "12C")
tk2 = copy.replace(tk, seat="14F")
print(tk, tk2)
# Ticket(gate='A1', seat='12C') Ticket(gate='A1', seat='14F')

Pass = namedtuple("Pass", "gate seat")
print(copy.replace(Pass("A1", "12C"), gate="B7"))
# Pass(gate='B7', seat='12C')

tk.seat = "1A"
# dataclasses.FrozenInstanceError: cannot assign to field 'seat'

Das Original tk bleibt, wie es ist, und es entsteht ein neues Objekt, in dem nur das geänderte Feld anders ist. Bei Objekten ohne Unterstützung (etwa Listen) kommt TypeError: replace() does not support list objects.

Fall abgeschlossen (10 min)

Sehen wir uns den Code der Begleitlehrkraft noch einmal an:

import copy
kit = [["towel", "notebook"], ["raincoat"]]
class1 = kit.copy()
class2 = kit.copy()
print(class1 is class2, class1[0] is class2[0])
# False True
class1[0][0] = "wet towel"
print(class2[0])
# ['wet towel', 'notebook']

Die äußeren Koffer waren verschieden, aber das Etikett im ersten Fach zeigte auf dieselbe Handtuchtasche. Das Wasser ist in genau diese eine Tasche gelaufen. Es gibt zwei Lösungen:

kit = [["towel", "notebook"], ["raincoat"]]
class1 = copy.deepcopy(kit)
class2 = copy.deepcopy(kit)
class1[0][0] = "wet towel"
print(class2[0], class1[0] is class2[0])
# ['towel', 'notebook'] False

def new_kit():
    return [["towel", "notebook"], ["raincoat"]]   # bei jedem Aufruf ein neuer Koffer
print(new_kit()[0] is new_kit()[0])
# False

Eine Funktion, die das Set packt, schafft das Original zum Kopieren ganz ab. Bei jedem Aufruf wird alles von vorn gepackt, also gibt es nichts, was geteilt werden könnte.

Als die Begleitlehrkraft gegangen ist, ruft Pin ein Röntgenbild von Band 13 auf. Die beiden Geisterkoffer sehen darauf aus, als steckten sie ineinander. Die Schichtleiterin tippt eine Zeile auf den Bildschirm:

a = []
b = [a]
a.append(b)
print(a)
# [[[...]]]

„Koffer, die einander enthalten. Vielleicht werden sie deshalb nicht weggeräumt, obwohl kein Etikett mehr dran hängt.“ Sie heftet das Bild in den Ordner.

Abschlussbericht — LOST-003. copy() war eine flache Kopie, beide Klassen teilten sich die innere Ausrüstungstasche. Gelöst mit copy.deepcopy oder einer Funktion, die jedes Set neu packt.

Übungen

  1. Was wird aus board = [[]] * 2, wenn du board[0].append("x") ausführst? Korrigiere es mit einer Comprehension.
  2. Nach d = {"a": [1]} und e = dict(d): Sag voraus, ob e["a"].append(2) und e["b"] = [3] jeweils in d sichtbar sind.
  3. Sei x = [1], y = [x, x], z = copy.deepcopy(y). Was ergibt z[0] is z[1]? Überleg auch, was ohne memo herausgekommen wäre.

Das Notizbuch der Schichtleiterin

  • Container enthalten keine Objekte, sondern Referenzen (Etiketten). Dasselbe Objekt kann in mehreren Fächern stecken.

  • [[0] * 3] * 3 ist dreimal dieselbe Liste. Zweidimensionale Listen baut man mit einer Comprehension.

  • Flache Kopie (lst[:], list(), .copy(), copy.copy): nur außen neu, innen geteilt.

  • Tiefe Kopie (copy.deepcopy): alles bis ganz innen neu. memo kopiert Geteiltes und Zyklen nur einmal.

  • Unveränderliche Objekte bleiben beim Kopieren dasselbe Objekt. Steckt Veränderliches darin, legt nur deepcopy neu an.

  • copy.replace(): neues Objekt, in dem nur einzelne Teile eines unveränderlichen Objekts anders sind (3.13).

← Zurück

Lektion 2 · Versiegelte Koffer: veränderlich und unveränderlich

Weiter →

Lektion 4 · Wenn ein Koffer übergeben wird: Funktionen und Argumente

Über neue Lektionen informiert werden

Wir schicken dir eine E-Mail, wenn dieser Kurs neue Lektionen bekommt oder ein Folgekurs erscheint.

Erhoben: deine E-Mail-Adresse · Zweck: Hinweise auf neue Lektionen oder den Start dieses Kurses · Speicherdauer: bis du die Löschung verlangst. Keine andere Nutzung, keine Weitergabe. Antworte einfach auf eine Benachrichtigung, dann löschen wir sie sofort.

Fragen & Antworten

Frag alles zu dieser Lektion. Fragen aus allen Sprachversionen dieser Seite erscheinen hier gemeinsam.

Wird als reiner Text angezeigt, ohne Links oder Formatierung.

R

ReflexKR

Python und Reflex zum Anfassen. Alle Beispiele wurden mit Reflex 0.10.0 ausgeführt und geprüft.

ReflexKR ist kostenlos und ohne Werbebanner. Wenn es dir geholfen hat, kannst du es mit einem Kaffee unterstützen. ☕ Spendier uns einen Kaffee

Hinweis zu Affiliate-Links
한국어English日本語简体中文EspañolDeutsch

© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0

Built with Reflex