问题
I wrote the following toy:
def foo():
x = 5
def foo2():
print("Locals: ", locals())
print("Vars: ", vars())
print("dir: ", dir())
print("CP 1")
print("x =", x)
print("CP 2")
print("Locals: ", locals())
print("Vars: ", vars())
print("dir: ", dir())
foo2()
foo()
print("-----------------------")
def foo():
x = 5
def foo2():
print("Locals: ", locals())
print("Vars: ", vars())
print("dir: ", dir())
print("CP 1")
print("x =", x)
print("CP 2")
del x
print("Locals: ", locals())
print("Vars: ", vars())
print("dir: ", dir())
foo2()
foo()
which produces the following output:
Locals: {'x': 5}
Vars: {'x': 5}
dir: ['x']
CP 1
x = 5
CP 2
Locals: {'x': 5}
Vars: {'x': 5}
dir: ['x']
-----------------------
Locals: {}
Vars: {}
dir: []
CP 1
Traceback (most recent call last):
File "testing.py", line 34, in <module>
foo()
File "testing.py", line 32, in foo
foo2()
File "testing.py", line 26, in foo2
print("x =", x)
UnboundLocalError: local variable 'x' referenced before assignment
>>>
Notice how behaviour of the second edition is modified even in the areas of the code up to which the two editions are identical (and thus should produce identical outcomes). Since x, according to the first edition, does exist in the local namespace, the del statement shouldn't be an issue.
Questions:
1) Is the first or second edition 'right'? Should x exist in the namespace or not?
2) Is there an explanation for this behaviour or is it a bug?
(Follow up to 2: 3) Should the second edition run without an error or is it supposed to crash?)
回答1:
The del x
triggers the interpreter into shadowing the non-local x
variable defined outside foo2()
's frame.
The same effect would be obtained if you were to replace del x
with x = ...
at the very same position.
The reason for this is that x
is actually on the same level as foo2()
and when del x
is reached during foo2()
, the interpreter decides not that the name x
should be reserved for a local variable x
, and hence it does not update your locals()
with the name from it's outer name. Moving the x
assignment inside foo2()
would let x
be truly local
and hence appear in locals()
:
def foo():
def foo2():
x = 5
print("Locals: ", locals())
print("Vars: ", vars())
print("dir: ", dir())
print("CP 1")
print("x =", x)
print("CP 2")
del x
print("Locals: ", locals())
print("Vars: ", vars())
print("dir: ", dir())
foo2()
foo()
Locals: {'x': 5}
Vars: {'x': 5}
dir: ['x']
CP 1
x = 5
CP 2
Locals: {}
Vars: {}
dir: []
as well as declaring x
to refer to the nonlocal
variable explicitly inside foo2()
:
def foo():
x = 5
def foo2():
nonlocal x
print("Locals: ", locals())
print("Vars: ", vars())
print("dir: ", dir())
print("CP 1")
print("x =", x)
print("CP 2")
del x
print("Locals: ", locals())
print("Vars: ", vars())
print("dir: ", dir())
foo2()
foo()
Locals: {'x': 5}
Vars: {'x': 5}
dir: ['x']
CP 1
x = 5
CP 2
Locals: {}
Vars: {}
dir: []
回答2:
The following post solved it for me:
https://amir.rachum.com/blog/2013/07/09/python-common-newbie-mistakes-part-2/
I would like highlight:
"The first misconception is that Python, being an interpreted language (which is awesome, I think we can all agree), is executed line-by-line. In truth, Python is being executed statement-by-statement. To get a feel of what I mean, go to your favorite shell (you aren’t using the default one, I hope) and type the following:
def foo():
Press Enter. As you can see, the shell didn’t offer any output and it’s clearly waiting for you to continue with your function definition."
This was the source of my confusion.
Thanks to @norok2 for pointing me to a post which pointed me to this.
(https://docs.python.org/3.1/faq/programming.html?highlight=nonlocal#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value
was also helpful)
来源:https://stackoverflow.com/questions/62033352/change-in-interpreted-behaviour-upon-introduction-of-del-keyword