14 quizzes to understand the surprisingly confusing scope of Python

introduction

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>.

Question 1

x = 1

if True:
    x = 2

print(x)

answer

2

In Python, the ʻifstatement does not form a scope. Therefore,x in the ʻif statement is the same variable as the outside x.

Problem 2

for i in range(10):
    x = i * 2

print(i, x)

answer

9 18

Like the ʻif, the forstatement does not form a scope, so loop variables and internally defined variables can be accessed after thefor` statement.

Problem 3

ls = [x * 2 for x in range(10)]

print(x)

answer

(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.

Problem 4

funs = []

for i in range(5):
    def f():
        print(i)
    funs.append(f)

for fun in funs:
    fun()

answer

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 fs are executed after the for statement, and since ʻiis4 at that point, they all output 4`.

The same thing happens with list comprehensions.

Problem 5

x = 0

def f():
    x = 1

f()
print(x)

answer

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.

Problem 6

x = 0

def f():
    print(x)
    x = 1

f()

answer

(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.

Problem 7

x = 0

def f():
    x += 1

f()
print(x)

answer

(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.

Problem 8

x = 0

def f():
    if False:
        x = 1
    print(x)

f()

answer

(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 tox in the block formed by fas in the previous problem, and the variablexis created. However, in reality, the value is not assigned tox`, so an exception occurs.

Problem 9 </ font>

x = 0

def f():
    del x

f()

answer

(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`.

Problem 10

x = 0

def f():
    def g():
        print(x)
    x = 1
    g()

f()

answer

1

When g is executed, 1 is output because x exists in the outer scope (f) and the value is 1.

Problem 11 </ font>

x = 0

def f():
    x = 1
    del x
    def g():
        print(x)
    g()

f()

answer

(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()

Problem 12 </ font>

x = 1

def f():
    x = 2
    try:
        raise Exception()
    except Exception as x:
        pass

    def g():
        print(x)
    g()

f()

answer

(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.

Problem 13

x = 1

class C:
    x = 2
    def f():
        print(x)
    f()

answer

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 **.

Problem 14 </ font>

x = 0

def f():
    x = 1
    def g():
        print(eval("x"))
    g()

f()

answer

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.

reference

Recommended Posts