Aufwärmen · Aufgabe 1 von 7
// B4.6 · ca. 25 Min. · Einstieg
Referenzen, Kopien und die Falle veränderlicher Standardwerte
Nach dieser Lektion sagen Sie vorher, wann eine Änderung an einer Variablen in einer anderen sichtbar wird, wählen die passende Art von Kopie und schreiben Standardargumente, die keinen Zustand zwischen Aufrufen weitergeben.
Lektion 6 von 6 in B4 Datenstrukturen
Danach können Sie
- Erklären, warum b = a eine Liste nicht kopiert, und is und == richtig einsetzen
- Für verschachtelte Daten zwischen Slice, copy.copy() und copy.deepcopy() wählen
- Ein veränderliches Standardargument durch das None-Muster ersetzen
Vorhersagen · Aufgabe 2 von 7
Sagen Sie voraus, bevor Sie es ausführen: Was gibt das aus?
a = [1, 2, 3] b = a b.append(4) print(a)Üben · Aufgabe 3 von 7
Ausgangspunkt ist a = [1, [2, 3]]. Ordnen Sie jeder Codezeile zu, was sie Ihnen liefert.
import copy a = [1, [2, 3]]Üben · Aufgabe 4 von 7
Was gibt das aus?
import copy grid = [[0, 0], [0, 0]] shallow = copy.copy(grid) deep = copy.deepcopy(grid) shallow[0][0] = 1 deep[1][1] = 9 print(grid)Üben · Aufgabe 5 von 7
Diese Funktion hat den Fehler mit dem veränderlichen Standardwert: Nach add_tag("a") liefert add_tag("b") beim zweiten Aufruf ['a', 'b']. Bringen Sie die Zeilen der korrigierten Fassung in die richtige Reihenfolge.
def add_tag(tag, tags=[]): tags.append(tag) return tags- 1. tags = []
- 2. tags.append(tag)
- 3. if tags is None:
- 4.def add_tag(tag, tags=None):
- 5. return tags
Denksport · Aufgabe 6 von 7
Knobelaufgabe. Die Zeile mit += löst einen Fehler aus, den der try-Block abfängt. Was gibt die letzte Zeile aus?
pair = (["foo"], "bar") try: pair[0] += ["item"] except TypeError as e: print("TypeError:", e) print(pair)Anwenden · Aufgabe 7 von 7
Mini-Aufgabe. Dieses Modul hat zwei Fehler mit Referenzen. Wenn Sie es ausführen, erhalten Sie ['reader', 'admin'] und dann ['start', 'stop'] ['start', 'stop']. Korrigieren Sie beide Funktionen, und fügen Sie assert-Zeilen hinzu, die jede Korrektur belegen.
def add_event(event, log=[]): log.append(event) return log def snapshot(state): return state.copy() state = {"user": "ana", "roles": ["reader"]} saved = snapshot(state) state["roles"].append("admin") print(saved["roles"]) print(add_event("start"), add_event("stop"))Prüfen Sie Ihr Ergebnis anhand dieser Liste
Selbst programmieren
Lesen Sie das ausgearbeitete Beispiel und lösen Sie dann die Übungen. Ihr Code läuft in Ihrem Browser oder auf Ihrem Computer und wird nie hochgeladen.
Ausgearbeitetes Beispiel
Eine Woche Einkaufslisten
Ein Dict mit Einkaufslisten, auf drei Arten erreichbar: über einen zweiten Namen, eine flache Kopie und eine tiefe Kopie. Dann ändert sich das Original, und die Ausgabe zeigt, welche der drei was davon sieht. Die Funktion oben nutzt None als Standardwert, jeder Aufruf ohne Korb bekommt also eine eigene Liste. Ändern Sie eine Zeile, und sagen Sie die Ausgabe voraus, bevor Sie es ausführen.
main.py
import copy
def add_item(item: str, basket: list[str] | None = None) -> list[str]:
if basket is None:
basket = [] # a new list for every call without a basket
basket.append(item)
return basket
week = {"mon": ["bread"], "tue": ["milk"]}
alias = week # a second name, not a copy
shallow = week.copy() # new dict, same inner lists
deep = copy.deepcopy(week) # new dict, new inner lists
week["mon"].append("eggs")
week["wed"] = ["tea"]
print(alias is week, alias == week, shallow is week)
print(shallow)
print(deep)
print(add_item("apple"), add_item("pear"))
mine = ["salt"]
print(add_item("rice", mine) is mine, mine)
Ausführen mit
python main.pyAusgabe
True True False
{'mon': ['bread', 'eggs'], 'tue': ['milk']}
{'mon': ['bread'], 'tue': ['milk']}
['apple'] ['pear']
True ['salt', 'rice']- alias ist dasselbe Dict wie week und sieht deshalb jede Änderung.
- shallow sieht "eggs", weil seine Montagsliste die Montagsliste von week ist, aber nicht den neuen Schlüssel "wed", weil es ein eigenes Dict ist.
- deep teilt nichts und zeigt die Woche noch so, wie sie war.
Ändern und ausführen
Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.
Beim ersten Ausführen lädt Ihr Browser Python herunter (bis zu 6.5 MB) und speichert es im Cache. Ihr Code bleibt auf Ihrem Gerät.
Übungen
Übung 1 von 2
Ein Korb pro Aufruf
add_item(item, basket) legt item in basket und gibt den Korb zurück; ohne Korb aufgerufen, soll die Funktion einen neuen anfangen. Der Starter hat den Fehler mit dem veränderlichen Standardwert: Der zweite Aufruf unten gibt ['apple', 'pear'] aus. Korrigieren Sie die Funktion, sodass jeder Aufruf ohne Korb eine neue Liste bekommt, während ein übergebener Korb, auch ein leerer, erweitert und zurückgegeben wird.
Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.
Beim ersten Ausführen lädt Ihr Browser Python herunter (bis zu 6.5 MB) und speichert es im Cache. Ihr Code bleibt auf Ihrem Gerät.
Hinweise
Hinweis 1
Die Standardliste wird einmal angelegt, wenn def läuft. Nehmen Sie stattdessen None als Standardwert.
Hinweis 2
Legen Sie in der Funktion nur dann eine neue Liste an, wenn basket None ist. if not basket würde auch einen leeren Korb ersetzen.
Hinweis 3
def add_item(item: str, basket: list[str] | None = None), dann if basket is None: basket = []
Eine Lösung zeigen
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
def add_item(item: str, basket: list[str] | None = None) -> list[str]:
"""Add item to basket and return it. Without a basket, start a new one."""
if basket is None:
basket = []
basket.append(item)
return basket
print(add_item("apple"))
print(add_item("pear"))
Auf dem eigenen Computer ausführen
Installieren Sie Python 3.14 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.
main.py
def add_item(item: str, basket: list[str] = []) -> list[str]:
"""Add item to basket and return it. Without a basket, start a new one."""
basket.append(item)
return basket
print(add_item("apple"))
print(add_item("pear"))
test_main.py
from main import add_item
def test_new_basket_each_call():
"""Zwei Aufrufe ohne Korb bekommen zwei verschiedene Listen"""
add_item("a")
got = add_item("b")
assert got == ["b"], f"der zweite Aufruf lieferte {got!r}, erwartet: ['b']"
def test_uses_given_basket():
"""Ein übergebener Korb wird erweitert und zurückgegeben"""
mine = ["x"]
got = add_item("a", mine)
assert got is mine, "add_item lieferte eine andere Liste, nicht den übergebenen Korb"
assert mine == ["x", "a"], f"der Korb ist {mine!r}, erwartet: ['x', 'a']"
def test_empty_basket_kept():
"""Ein übergebener leerer Korb wird benutzt, nicht ersetzt"""
mine: list[str] = []
got = add_item("a", mine)
assert got is mine, "ein leerer Korb wurde durch eine neue Liste ersetzt; prüfen Sie mit is None"
Unter macOS und Linux tippen Sie python3, wo in diesen Befehlen python steht, wie in der ersten Lektion.
Programm ausführen:
python main.pyPrüfungen ausführen (learnrun.py muss im selben Ordner liegen):
python learnrun.py testlearnrun.py herunterladenÜbung 2 von 2
Gitter, die nichts teilen
make_grid(rows, cols) soll ein Gitter aus Nullen liefern, dessen Zeilen eigene Listen sind, und copy_grid(grid) eine Kopie, die keine Zeile mit dem Original teilt. Der Starter nutzt * für die Zeilen und copy() für die Kopie, und beide teilen Zeilen: Das Programm unten gibt [[1, 0, 0], [1, 0, 0]] aus. Korrigieren Sie beide Funktionen.
Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.
Beim ersten Ausführen lädt Ihr Browser Python herunter (bis zu 6.5 MB) und speichert es im Cache. Ihr Code bleibt auf Ihrem Gerät.
Hinweise
Hinweis 1
[[0] * cols] * rows wiederholt einen Verweis auf eine einzige Zeile. Eine List Comprehension führt [0] * cols für jede Zeile neu aus.
Hinweis 2
grid.copy() ist flach. Kopieren Sie auch jede Zeile, mit einem Slice oder mit copy.deepcopy().
Hinweis 3
return [[0] * cols for _ in range(rows)] und return [row[:] for row in grid]
Eine Lösung zeigen
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
def make_grid(rows: int, cols: int) -> list[list[int]]:
"""Return a rows x cols grid of zeros whose rows are separate lists."""
return [[0] * cols for _ in range(rows)]
def copy_grid(grid: list[list[int]]) -> list[list[int]]:
"""Return a copy of grid that shares no row with it."""
return [row[:] for row in grid]
board = make_grid(2, 3)
board[0][0] = 1
print(board)
Auf dem eigenen Computer ausführen
Installieren Sie Python 3.14 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.
main.py
def make_grid(rows: int, cols: int) -> list[list[int]]:
"""Return a rows x cols grid of zeros whose rows are separate lists."""
return [[0] * cols] * rows
def copy_grid(grid: list[list[int]]) -> list[list[int]]:
"""Return a copy of grid that shares no row with it."""
return grid.copy()
board = make_grid(2, 3)
board[0][0] = 1
print(board)
test_main.py
from main import copy_grid, make_grid
def test_grid_shape():
"""make_grid(2, 3) sind zwei Zeilen mit je drei Nullen"""
got = make_grid(2, 3)
assert got == [[0, 0, 0], [0, 0, 0]], f"make_grid(2, 3) lieferte {got!r}"
def test_rows_independent():
"""Eine Zeile eines neuen Gitters zu ändern lässt die anderen Zeilen unverändert"""
grid = make_grid(3, 2)
grid[0][0] = 1
assert grid == [[1, 0], [0, 0], [0, 0]], f"nach grid[0][0] = 1 ist das Gitter {grid!r}; die Zeilen sind eine gemeinsame Liste"
def test_copy_equal():
"""Die Kopie hat dieselben Werte"""
grid = [[1, 2], [3, 4]]
got = copy_grid(grid)
assert got == grid, f"copy_grid lieferte {got!r}, erwartet: [[1, 2], [3, 4]]"
def test_copy_independent():
"""Die Kopie zu ändern, auch innerhalb einer Zeile, lässt das Original unverändert"""
grid = [[1, 2], [3, 4]]
new = copy_grid(grid)
new[0][0] = 9
assert grid == [[1, 2], [3, 4]], f"das Original wurde zu {grid!r}; die Kopie teilt noch seine Zeilen"
Unter macOS und Linux tippen Sie python3, wo in diesen Befehlen python steht, wie in der ersten Lektion.
Programm ausführen:
python main.pyPrüfungen ausführen (learnrun.py muss im selben Ordner liegen):
python learnrun.py testlearnrun.py herunterladenHäufige Fehler
Ein „Backup“ mit b = a anlegen
scores = [3, 5]
backup = scores
scores.clear()
print(backup[0])
Was Python ausgibt
IndexError: list index out of rangeWarum, und die Lösung
backup = scores ist ein zweiter Name für dieselbe Liste, clear() leert also beide. Machen Sie eine Kopie, bevor Sie das Original ändern: backup = scores.copy().
Eine flache Kopie verschachtelter Daten
settings = {"theme": {"dark": False}}
backup = settings.copy()
settings["theme"]["dark"] = True
assert backup["theme"]["dark"] is False, "the backup changed too"
Was Python ausgibt
AssertionError: the backup changed tooWarum, und die Lösung
settings.copy() macht ein neues äußeres Dict, aber backup["theme"] ist noch dasselbe innere Dict. Für verschachtelte Daten nehmen Sie copy.deepcopy(settings).
Den Standardwert mit if not prüfen
def add_tag(tag, tags=None):
if not tags:
tags = []
tags.append(tag)
return tags
mine = []
assert add_tag("a", mine) is mine, "the caller's list was replaced"
Was Python ausgibt
AssertionError: the caller's list was replacedWarum, und die Lösung
Eine leere Liste gilt als falsch, also ersetzt if not tags die Liste, die der Aufrufer übergeben hat. Prüfen Sie auf den Standardwert selbst: if tags is None.
Python im Browser: Pyodide 314.0.7, MPL-2.0. Lizenz und Quellcode
Abschlussquiz
5 Fragen, ohne Hinweise. Ab 80 % ist die Lektion abgeschlossen.
Erledigen Sie zuerst alle Aufgaben oben, um das Abschlussquiz freizuschalten.