Seite 1 von 1
Spaß mit Dekoratoren
Verfasst: Mittwoch 28. Juni 2023, 20:59
von pillmuncher
Code: Alles auswählen
def foo(x, y, z, register_undo):
# irgendwas mit x, y, z machen
@register_undo
def undo_foo():
# irgendwas mit x, y, z wieder rückgängig machen
def main():
undo_stack = []
...
foo(a, b, c, undo_stack.append)
...
undo_stack.pop()()
if __name__ == "__main__":
main()
Re: Spaß mit Dekoratoren
Verfasst: Mittwoch 15. November 2023, 23:37
von mm96
Hi,
ich verstehe Dein Beispiel nicht richtig, ich kriegs auch nicht zum Laufen.
Z. Bsp. der Code
Code: Alles auswählen
def foo(x, y, z, register_undo):
x +=5
@register_undo
def undo_foo():
x -=5
def main():
a, b, c = 1, 2, 3
undo_stack = []
foo(a, b, c, undo_stack.append)
undo_stack.pop()()
if __name__ == "__main__":
main()
wirft den Error in Zeile 7:
Code: Alles auswählen
UnboundLocalError: cannot access local variable 'x' where it is not associated with a value
Könntest Du mir da auf die Sprünge helfen?
LG
Re: Spaß mit Dekoratoren
Verfasst: Donnerstag 16. November 2023, 00:58
von __blackjack__
@mm96: Zahlen sind nicht veränderbar, das heisst `foo()` macht mit dem `x` nichts und da kann man auch nichts rückgängig machen.
Re: Spaß mit Dekoratoren
Verfasst: Donnerstag 16. November 2023, 01:06
von pillmuncher
Das
x in
foo() und das
x in
undo_foo() sind nicht dasselbe
x. Python kennt keinen
Lexical Scope. Vergleiche:
Code: Alles auswählen
# modul xyz
x = 1
def bar():
x = 2
print(x)
bar()
print(x)
Ergebnis:
Das funktioniert, weil die Variable
x in
bar() bei der Zuweisung "entsteht". Dein
x -= 5 bedeutet aber: "Nimm die
bereits lokal bestehende Variable
x, subtrahiere 5 und weise den Wert anschließend wieder
x zu. Das geht natürlich nicht, weil im lokalen Scope kein
x existiert. Du lönntest natürlich eine
nonlocal Anweisung verwenden, was aber ggf. den Programmfluss ziemlich unübersichtlich macht. Der übliche Weg, wie man in Python langlebige Werte verwendet, ist mit Klassen und Objekten:
Code: Alles auswählen
class Baz:
def __init__(self):
self.x = 123
self._undo_list = []
def undo(self):
if self._undo_list:
self._undo_list.pop()
def foo(self):
self.x += 5
@self._undo_list.append
def undo_foo():
self.x -= 5
b = Baz()
print(b.x) # -> 123
b.foo()
print(b.x) # -> 128
b.undo()
print(b.x) # -> 123
Ungetestet.
Re: Spaß mit Dekoratoren
Verfasst: Donnerstag 16. November 2023, 08:52
von kbr
Um zur Verwirrung beizutragen: Aufgrund der closure ist Zugriff auf x möglich:
Eine Dekrementierung via `x -= 5` ist jedoch mit der Erzeugung des Labels `x` im lokalen Scope verbunden und das führt dann zu dem genannten Fehler. Dem lässt sich mit `nonlocal` beikommen (wie pillmuncher schon schrieb) mit der entsprechenden Konfusion, die damit einhergehen kann.
Re: Spaß mit Dekoratoren
Verfasst: Dienstag 19. Dezember 2023, 22:34
von mm96
Ok, ich glaub ich habs endlich kapiert

.
Danke Euch!