Klasse 11 · Informatik · Doppelstunde 1

Wie denkt Python
über Daten?

Variablen, Referenzen, Speicheradressen — ein Blick hinter die Kulissen von Python und dynamischen Datentypen.

Speichermodell Referenzen & Aliasing Dynamische Typen
Modul 01

Statisch vs. Dynamisch

Nicht alle Programmiersprachen gehen gleich mit Speicher um. Python unterscheidet sich grundlegend von Sprachen wie C oder Java — aber was genau bedeutet das?

Statische Typisierung

  • Typ einer Variable wird beim Compilieren festgelegt
  • Speicher wird zur Compilezeit reserviert
  • Typfehler werden früh erkannt
  • Variable kann nur Werte des deklarierten Typs speichern
  • Beispiele: C, Java, C++, Rust
  • int x = 5; — x ist für immer int

Dynamische Typisierung

  • Typ wird zur Laufzeit bestimmt — nicht vorher
  • Speicher wird flexibel zur Laufzeit reserviert
  • Eine Variable kann ihren Typ wechseln
  • Schnelleres Prototyping, aber spätere Fehler
  • Beispiele: Python, JavaScript, Ruby
  • x = 5 → dann x = "Hallo"

🗃️ Statisches Array

In C oder Java ist ein Array ein zusammenhängender Speicherblock fester Größe.

↓ Details anzeigen
Beispiel in C:
int zahlen[5] = {1,2,3,4,5}; Das reserviert genau 5 × 4 Byte = 20 Byte im Speicher. Die Größe kann danach nicht geändert werden. Braucht man mehr Platz, muss manuell ein neues, größeres Array angelegt und der Inhalt kopiert werden.

🐍 Python-Liste

Pythons Liste ist ein dynamisches Objekt — sie wächst und schrumpft automatisch.

↓ Details anzeigen
Python-Listen sind keine Arrays im klassischen Sinn. Intern speichert Python eine Liste von Referenzen (Zeigern) auf Objekte. Jedes Element kann einen anderen Typ haben. Das Wachsen der Liste (append) funktioniert über Überallokation: Python reserviert mehr Platz als gerade nötig, um künftige Erweiterungen zu beschleunigen.

📦 Speichermodell

Python-Variablen sind keine Behälter für Werte — sie sind Namen, die auf Objekte zeigen.

↓ Details anzeigen
In Python gibt es zwei Bereiche:

Stack: Lokale Variablennamen (Referenzen) werden hier verwaltet.

Heap: Die eigentlichen Objekte (Zahlen, Listen, Strings…) leben hier. Python's Garbage Collector kümmert sich automatisch um nicht mehr benötigte Heap-Objekte.

🔢 Alles ist ein Objekt

In Python ist sogar die Zahl 5 ein vollständiges Objekt mit Typ, Wert und ID.

↓ Details anzeigen
Jedes Python-Objekt hat drei Eigenschaften:

Wert (z.B. 42)
Typ (z.B. int)
Identität / Adresse — abfragbar mit id(x)

Python cached kleine ganze Zahlen (−5 bis 256) und kurze Strings — das erklärt einige überraschende id()-Ergebnisse!
Modul 02

Das Python-Speichermodell

Was passiert wirklich, wenn du a = 5 schreibst? Python legt ein int-Objekt im Heap an und speichert eine Referenz darauf unter dem Namen a.

Schritt 0 / 4
Variablenraum (Stack)
Objekte im Heap
Klicke auf einen Schritt, um zu sehen, was im Speicher passiert.

🔬 Ausprobieren: id()

Weise Variablen zu und beobachte die Speicheradressen. Ändere Werte und schau, was passiert.

speicher_id.py
⚠ input() nicht verfügbar
Ausgabe
— noch keine Ausgabe —
Modul 03

Aliasing & Kopieren

Im Speichermodell (Modul 2) haben wir gesehen, wie b = a bei Integers funktioniert. Bei Listen verhält sich Python jedoch anders — und das führt zu einer der häufigsten Fehlerquellen.

Schritt 1a — Rückblick: b = a bei Integers

🔢 Integers sind immutable

Unveränderlich — ein Integer-Objekt kann nach seiner Erstellung nicht verändert werden. a = 10 erzeugt ein neues Objekt, das alte bleibt unberührt.

↓ Details anzeigen
Das haben wir im Visualizer gesehen: Nach b = a zeigen beide auf dasselbe Objekt. Sobald aber a = 10 ausgeführt wird, zeigt a auf ein neues Objekt — b bleibt beim alten.

Immutable Typen in Python: int, float, str, tuple, bool

Schritt 1b — Anders bei Listen: mutable

📋 Listen sind mutable

Veränderlich — das Listen-Objekt selbst kann verändert werden, ohne dass ein neues Objekt entsteht. b = a erzeugt daher einen Alias: beide Namen zeigen auf dasselbe Objekt.

↓ Details anzeigen
Wenn b ein Alias für a ist und man b.append(4) aufruft, wird das Listenobjekt direkt im Heap verändert — nicht b selbst. Da a auf dasselbe Objekt zeigt, sieht a die Änderung sofort.

Mutable Typen in Python: list, dict, set

🔬 Sieh den Unterschied selbst — vergleiche Integer- und Listen-Aliasing:

aliasing_listen.py
⚠ input() nicht verfügbar
Ausgabe
— noch keine Ausgabe —

Schritt 1c — Testen: mutable vs. immutable

💭 Wahr oder Falsch?

b = a verhält sich bei einer Liste genauso wie bei einem Integer — b wird unabhängig von a.

→ Klicken zum Aufdecken
❌ Falsch — bei Integers entsteht durch Neuzuweisung ein neues Objekt (immutable). Bei Listen zeigt b auf dasselbe Objekt — Änderungen über b sind sofort über a sichtbar (mutable).
💭 Wahr oder Falsch?

Nach a = [1,2,3]; b = a; a = [9,9,9] zeigt b noch auf [1,2,3].

→ Klicken zum Aufdecken
✅ Richtig — a = [9,9,9] lässt a auf ein neues Listenobjekt zeigen. b bleibt beim alten Objekt. Achtung: anders wäre es bei a[0] = 9 — das würde das Objekt direkt verändern.
💭 Wahr oder Falsch?

Strings verhalten sich wie Listen — s += "!" hängt das Ausrufezeichen direkt an das bestehende String-Objekt an.

→ Klicken zum Aufdecken
❌ Falsch — Strings sind immutable. s += "!" erstellt ein neues String-Objekt. Das alte Objekt bleibt unverändert im Heap — nur s zeigt danach auf das neue.
💭 Wahr oder Falsch?

Tuples sind immutable — t = (1,2,3); t[0] = 9 wirft einen Fehler.

→ Klicken zum Aufdecken
✅ Richtig — Tuples sind unveränderlich. Der Zugriff t[0] zum Lesen funktioniert, aber t[0] = 9 wirft einen TypeError. Tuples sind damit die „eingefrorene" Variante einer Liste.

Schritt 2 — Echte Kopie mit copy()

📋 Flache Kopie — copy.copy()

Erstellt ein neues Listenobjekt mit denselben Elementen. Änderungen an der Kopie beeinflussen das Original nicht — und umgekehrt.

↓ Details anzeigen
import copy a = [10, 20, 30] b = copy.copy(a) # neues Objekt! b.append(40) print(a) # [10, 20, 30] ← unverändert print(b) # [10, 20, 30, 40] Kurzschreibweise: Statt copy.copy(a) geht auch a[:] oder list(a) — beides erzeugt ebenfalls eine flache Kopie.

🔬 Überprüfe deine Vorhersage: Was gibt der Code aus?

copy_einfach.py
⚠ input() nicht verfügbar
Ausgabe
— noch keine Ausgabe —

Schritt 3 — Das Problem: verschachtelte Listen

⚠️ copy() reicht nicht

Bei Listen-in-Listen kopiert copy() nur die äußere Liste. Die inneren Listen werden weiterhin geteilt.

↓ Details anzeigen
a = [1, [2, 3]] b = copy.copy(a) b[1].append(99) print(a) # [1, [2, 3, 99]] ← !!! Die äußere Liste ist neu — aber a[1] und b[1] zeigen noch auf dasselbe innere Listen-Objekt.

✅ Lösung: deepcopy()

Kopiert alles rekursiv — auch alle inneren Objekte. Original und Kopie sind vollständig unabhängig.

↓ Details anzeigen
a = [1, [2, 3]] b = copy.deepcopy(a) b[1].append(99) print(a) # [1, [2, 3]] ← unberührt Wann nötig? Immer bei verschachtelten Strukturen (Listen in Listen usw.) wenn beide Seiten unabhängig sein sollen.

🔬 Sieh selbst: Wo macht copy() den Unterschied?

copy_vs_deepcopy.py
⚠ input() nicht verfügbar
Ausgabe
— noch keine Ausgabe —
Modul 04

Listenoperationen

Du kennst die wichtigsten Operationen bereits — jetzt verbindest du sie mit dem Speichermodell. Was passiert intern, wenn du append() oder insert() aufrufst?

🎯 Zuordnen: Operation → Beschreibung

Operationen (ziehen)
  • list.append(x)
  • list.insert(i, x)
  • list.pop(i)
  • list.remove(x)
  • list.index(x)
  • len(list)
  • list.extend(l)
  • list.sort()
  • list.reverse()
  • list.count(x)
Beschreibungen (Ziel)
Fügt x ans Ende der Liste an. Intern: falls kein Platz, neuen Speicherblock anlegen.
Fügt x an Position i ein. Alle nachfolgenden Elemente werden verschoben.
Entfernt und gibt das Element an Index i zurück. Ohne Argument: letztes Element.
Sucht das erste Vorkommen von x und entfernt es. Wirft ValueError wenn nicht gefunden.
Gibt den Index des ersten Vorkommens von x zurück.
Gibt die Anzahl der Elemente zurück. O(1) — direkt gespeichert.
Hängt alle Elemente einer anderen Liste ans Ende an. Wie mehrfaches append(), aber effizienter.
Sortiert die Liste aufsteigend — direkt im Objekt (in-place). Gibt None zurück.
Kehrt die Reihenfolge der Elemente um — in-place. Gibt None zurück.
Zählt, wie oft x in der Liste vorkommt. Gibt 0 zurück wenn nicht vorhanden.

🔬 Freier Playground: Listen & Speicher

Experimentiere mit Listenoperationen. Versuche z.B. sys.getsizeof() um zu sehen, wie sich der Speicherbedarf einer Liste beim Wachsen verändert.

listen_playground.py
⚠ input() nicht verfügbar
Ausgabe
— noch keine Ausgabe —

Wissen testen

8 Fragen in vier Schwierigkeitsstufen — von Grundwissen bis kritischem Denken.

① Grundwissen
② Verständnis
③ Anwendung
④ Kritisch
0/8
Punkte erreicht

Ist Aliasing ein Bug oder Feature?

„In Python zeigen Variablen nicht auf Werte — sie zeigen auf Objekte. Das ist kein Fehler im Design, sondern eine bewusste Entscheidung für Effizienz und Flexibilität."
Pro Aliasing Speichereffizient: große Objekte werden nicht unnötig kopiert. Funktionen können Listen direkt verändern — kein Rückgabewert nötig.
Contra Aliasing Unerwartete Seiteneffekte: Änderungen an einem Alias sind sofort für alle sichtbar. Debugging wird schwieriger — wer hat meine Liste verändert?
Diskutiert in der Klasse Wann würdest du bewusst einen Alias verwenden? Wann ist eine explizite Kopie die bessere Wahl? Gibt es Situationen, wo deepcopy ein Zeichen für schlechtes Design ist?