Now that we are familiar with all types of comprehensions and generator expression, let's talk about name localization within them. Python 3.* localizes loop variables in all four forms of comprehensions: list
, dict
, set
, and generator expressions. This behavior is therefore different from that of the for
loop. Let's see a simple example to show all the cases:
scopes.py
A = 100 ex1 = [A for A in range(5)] print(A) # prints: 100 ex2 = list(A for A in range(5)) print(A) # prints: 100 ex3 = dict((A, 2 * A) for A in range(5)) print(A) # prints: 100 ex4 = set(A for A in range(5)) print(A) # prints: 100 s = 0 for A in range(5): s += A print(A) # prints: 4
In the preceding code, we declare a global name A = 100
, and then we exercise the four comprehensions: list, generator expression, dictionary, and set. None of them alter the global name A
. Conversely, you can see at the end that the for
loop modifies it. The last print statement prints 4.
Let's see what happens if A
wasn't there:
scopes.noglobal.py
ex1 = [A for A in range(5)] print(A) # breaks: NameError: name 'A' is not defined
The preceding code would work the same with any of the four types of comprehensions. After we run the first line, A
is not defined in the global namespace.
Once again, the for
loop behaves differently:
scopes.for.py
s = 0 for A in range(5): s += A print(A) # prints: 4 print(globals())
The preceding code shows that after a for
loop, if the loop variable wasn't defined before it, we can find it in the global frame. To make sure of it, let's take a peek at it by calling the globals()
built-in function:
$ python scopes.for.py 4 {'__spec__': None, '__name__': '__main__', 's': 10, 'A': 4, '__doc__': None, '__cached__': None, '__package__': None, '__file__': 'scopes.for.py', '__loader__': <_frozen_importlib.SourceFileLoader object at 0x7f05a5a183c8>, '__builtins__': <module 'builtins' (built-in)>}
Together with a lot of other boilerplate stuff, we can spot 'A': 4
.