Python's variable scope and reference mechanism are surprisingly unintuitive, and beginners can fall into the trap. But once you learn the rules, it's not that complicated. Here are some issues to help you understand that.
For each question, answer ** what is output or whether an error is output **. The execution environment is Python 3. The difficult (or rather maniac) problem is that the heading is red </ font>.
x = 1
if True:
x = 2
print(x)
2
In Python, the ʻifstatement does not form a scope. Therefore,
x in the ʻif
statement is the same variable as the outside x
.
for i in range(10):
x = i * 2
print(i, x)
9 18
Like the ʻif, the
forstatement does not form a scope, so loop variables and internally defined variables can be accessed after the
for` statement.
ls = [x * 2 for x in range(10)]
print(x)
(print(x)At the place)
NameError: name 'x' is not defined
When list comprehension is executed, a new scope is created and loop variables are defined there. Therefore, X
cannot be accessed fromprint (x)
existing in the outer scope.
funs = []
for i in range(5):
def f():
print(i)
funs.append(f)
for fun in funs:
fun()
4
4
4
4
4
The outer variable ʻi is referenced from within the function
f, but the value of ʻi
at the time ** print (i)
is executed is used **. All five f
s are executed after the for
statement, and since ʻiis
4 at that point, they all output
4`.
The same thing happens with list comprehensions.
x = 0
def f():
x = 1
f()
print(x)
0
Since the block [^ block] formed by the function f
has a ** assignment statement ** of x = 1
, a new variable of x
will be created in the scope of this block. That is, the top-level x
is not modified by f
.
[^ block]: block is a set of code corresponding to one scope. , A scope is formed for each block. It is the functions, modules (ie top level), and class definitions that form the blocks.
x = 0
def f():
print(x)
x = 1
f()
(print(x)At the place)
UnboundLocalError: local variable 'x' referenced before assignment
Similar to the previous problem, the block formed by the function f
has a ** assignment statement ** that says x = 1
, so the scope is x before ** before the block is executed. A variable called x
is created. But when print (x)
is executed, x
is unbound, so an exception is thrown.
x = 0
def f():
x += 1
f()
print(x)
(print(x)At the place)
UnboundLocalError: local variable 'x' referenced before assignment
Since + =
is also considered as an assignment statement, the variable x
is created in the block formed by f
as in the previous problem. However, when x + = 1
is executed, the value of x
does not exist, so an exception is thrown.
You need to use global
or nonlocal
to change the value of variables in the outer scope.
x = 0
def f():
if False:
x = 1
print(x)
f()
(print(x)At the place)
UnboundLocalError: local variable 'x' referenced before assignment
Since the ʻifstatement does not form a block, there is an assignment statement to
x in the block formed by
fas in the previous problem, and the variable
xis created. However, in reality, the value is not assigned to
x`, so an exception occurs.
x = 0
def f():
del x
f()
(At the place of del x)
UnboundLocalError: local variable 'x' referenced before assignment
In fact, the del
statement has the effect of creating a variable in that block, just like the assignment statement. Therefore, the variable x
is created in the block formed by f
, but an exception occurs because del x
is executed without the value of x
exists.
The syntax that has the effect of creating variables in this way is the assignment statement, the del
statement, the for
statement, the class definition, the function definition, the ʻimport statement, and the
with and ʻexcept
ʻas`.
x = 0
def f():
def g():
print(x)
x = 1
g()
f()
1
When g
is executed, 1
is output because x
exists in the outer scope (f
) and the value is 1
.
x = 0
def f():
x = 1
del x
def g():
print(x)
g()
f()
(print(x)At the place)
NameError: free variable 'x' referenced before assignment in enclosing scope
Even if del x
is executed, x
continues to exist in the scope of f
(only the value disappears). Therefore, x
in g
refers to x
defined by f
. But that x
has no value, so an exception is thrown.
For the same reason, the following two codes give the same result.
x = 0
def f():
x = 1
def g():
print(x)
del x
g()
f()
x = 0
def f():
def g():
print(x)
x = 1
del x
g()
f()
x = 1
def f():
x = 2
try:
raise Exception()
except Exception as x:
pass
def g():
print(x)
g()
f()
(print(x)At the place)
NameError: free variable 'x' referenced before assignment in enclosing scope
When an exception occurs in the try
statement and the ʻexcept clause is executed, the variable specified by the ʻas
is ** deleted ** as in the del
. Therefore, when g
is executed, x
in f
exists but has no value, so an exception occurs.
If the ʻexcept` clause is not executed, the corresponding variable will not be deleted.
x = 1
class C:
x = 2
def f():
print(x)
f()
1
The code in a class definition is special about scope, and variables defined there (class variables) can only be accessed from that scope, ** not directly from functions (that is, methods) in the class definition **.
x = 0
def f():
x = 1
def g():
print(eval("x"))
g()
f()
0
This is a problem with the (intuitive) behavior of the ʻeval function. The only variables that can be accessed from the code executed by the ʻeval
function are actually **" global variables "and" variables defined in the block where ʻeval is executed **", and the variables defined between them Is inaccessible. In the current code, the variables (other than the built-in) that can be accessed from the code passed to the ʻeval
function are the variables (not even one) defined in the x
and g
on the first line. Therefore, the top-level x
is referenced and 0
is output.
The ʻexec` function behaves in the same way.
Recommended Posts