Question

I am wondering if there is a way to avoid that for-loop variables cut into Python's global namespace?

The only solution I could come up with so far would be using closures, such as list comprehensions

E.g., for the following code:

i = 1
print([i for i in range(5)])
print(i, 'i in global')

j = 1
for j in range(5):
    if j == 4:
        print(j, 'j in for-loop')
print(j, 'j in global')

prints

[0, 1, 2, 3, 4]
1 i in global
4 j in for-loop
4 j in global

EDIT:
I assume that the for-loop namespace falls into the categoy "Global", unlike a function ("Local") in the LEGB rule, and thus there might be no solution to that? Btw., do Python 2.x comprehensions do not have their own namespace? since it was pointed out in the comments that Python 2.x would print "4 in global" ( I am using Python 3.4)

Was it helpful?

Solution

for loops don't have their own scope in Python; there is only surrounding scope. So any variable defined in the for expression will persist in that scope. They're only being added into the global scope because you're running your code outside of a function. If you add it into a function, you'll see it doesn't "leak":

def loop():
    for j in range(5):
        pass

>>> loop()
>>> 'j' in globals()
False

In Python 2.x, list comprehensions had the exact same behaviour: they would add the iterated variable into the local scope, as they were fundamentally syntactic sugar for the for construct. As this seemed to continually surprise people, it was changed for Python 3.x so that the variables aren't added to the local scope.

Also note that list comprehensions have different semantics: they are closer to syntactic sugar for a generator expression inside a list() constructor, and in particular the loop control variables are no longer leaked into the surrounding scope.

What's New In Python 3.0

The scope behaviour of for loops is useful for patterns like the following:

# find line number in file with matching text
for i, line in enumerate(some_file):
    if 'some text' in line:
        break
print('First match found at line {}.'.format(i))
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top