Python: LOAD_FAST vs. LOAD_DEREF mit Inplace-Zusatz
Letzten Freitag ging ich zu einem Vorstellungsgespräch und musste die folgende Frage beantworten: Warum löst dieser Code eine Ausnahme aus UnboundLocalError: local variable 'var' referenced before assignment
in der Zeile mitvar += 1
)?
def outer():
var = 1
def inner():
var += 1
return var
return inner
Ich konnte keine richtige Antwort geben; Diese Tatsache hat mich sehr verärgert, und als ich nach Hause kam, habe ich mich sehr bemüht, eine richtige Antwort zu finden. Nun, ichhabe fand die Antwort, aber jetzt gibt es noch etwas, das mich verwirrt.
Ich muss im Voraus @ sag dass meine Frage eher die Entscheidungen betrifft, die beim Entwerfen der Sprache getroffen wurden, als die Funktionsweise.
Also, betrachten Sie diesen Code. Die innere Funktion ist ein Python-Verschluss undvar
ist nicht lokal fürouter
- es wird in einer Zelle gespeichert (und dann aus einer Zelle abgerufen):
def outer():
var = 1
def inner():
return var
return inner
Die Demontage sieht folgendermaßen aus:
0 LOAD_CONST 1 (1)
3 STORE_DEREF 0 (var) # not STORE_FAST
6 LOAD_CLOSURE 0 (var)
9 BUILD_TUPLE 1
12 LOAD_CONST 2 (<code object inner at 0x10796c810)
15 LOAD_CONST 3 ('outer.<locals>.inner')
18 MAKE_CLOSURE 0
21 STORE_FAST 0 (inner)
24 LOAD_FAST 0 (inner)
27 RETURN_VALUE
recursing into <code object inner at 0x10796c810:
0 LOAD_DEREF 0 (var) # same thing
3 RETURN_VALUE
Dies ändert sich, wenn wir versuchen, etwas anderes an @ zu bindevar
innerhalb der inneren Funktion:
def outer():
var = 1
def inner():
var = 2
return var
return inner
ochmals die Demontage:
0 LOAD_CONST 1 (1)
3 STORE_FAST 0 (var) # this one changed
6 LOAD_CONST 2 (<code object inner at 0x1084a1810)
9 LOAD_CONST 3 ('outer.<locals>.inner')
12 MAKE_FUNCTION 0 # AND not MAKE_CLOSURE
15 STORE_FAST 1 (inner)
18 LOAD_FAST 1 (inner)
21 RETURN_VALUE
recursing into <code object inner at 0x1084a1810:
0 LOAD_CONST 1 (2)
3 STORE_FAST 0 (var) # 'var' is supposed to be local
6 LOAD_FAST 0 (var)
9 RETURN_VALUE
Wir speichernvar
local, was den Angaben in der Dokumentation entspricht:Namenzuweisungen gehen immer in den innersten Bereich.
Nun, wenn wir versuchen, ein Inkrement zu machenvar += 1
, eine böseLOAD_FAST
erscheint, der versucht, @ zu bekommvar
voninner
okaler Geltungsbereich von @:
14 LOAD_FAST 0 (var)
17 LOAD_CONST 2 (2)
20 INPLACE_ADD
21 STORE_FAST 0 (var)
Und natürlich bekommen wir einen Fehler. Jetzt,hier ist was ich nicht bekomme: warum können wir nicht abrufenvar
mit einerLOAD_DEREF
, und dann speichern Sie es ininner
's Umfang mit einemSTORE_FAST
? Ich meine, das scheint OK zu sein. mit dem "innersten Bereich" Zuweisungsmaterial, und gleichzeitig ist es etwas intuitiver wünschenswert. Zumindest das+=
code würde tun, was wir wollen, und ich kann mir keine Situation einfallen lassen, in der der beschriebene Ansatz etwas durcheinander bringen könnte.
Können Sie? Ich habe das Gefühl, dass mir hier etwas fehlt.