Scoping in Python 'for' loops

PythonScope

Python Problem Overview


I'm not asking about Python's scoping rules; I understand generally how scoping works in Python for loops. My question is why the design decisions were made in this way. For example (no pun intended):

for foo in xrange(10):
    bar = 2
print(foo, bar)

The above will print (9,2).

This strikes me as weird: 'foo' is really just controlling the loop, and 'bar' was defined inside the loop. I can understand why it might be necessary for 'bar' to be accessible outside the loop (otherwise, for loops would have very limited functionality). What I don't understand is why it is necessary for the control variable to remain in scope after the loop exits. In my experience, it simply clutters the global namespace and makes it harder to track down errors that would be caught by interpreters in other languages.

Python Solutions


Solution 1 - Python

The likeliest answer is that it just keeps the grammar simple, hasn't been a stumbling block for adoption, and many have been happy with not having to disambiguate the scope to which a name belongs when assigning to it within a loop construct. Variables are not declared within a scope, it is implied by the location of assignment statements. The global keyword exists just for this reason (to signify that assignment is done at a global scope).

Update

Here's a good discussion on the topic: http://mail.python.org/pipermail/python-ideas/2008-October/002109.html

> Previous proposals to make for-loop > variables local to the loop have > stumbled on the problem of existing > code that relies on the loop variable > keeping its value after exiting the > loop, and it seems that this is > regarded as a desirable feature.

In short, you can probably blame it on the Python community :P

Solution 2 - Python

Python does not have blocks, as do some other languages (such as C/C++ or Java). Therefore, scoping unit in Python is a function.

Solution 3 - Python

A really useful case for this is when using enumerate and you want the total count in the end:

for count, x in enumerate(someiterator, start=1):
    dosomething(count, x)
print "I did something {0} times".format(count)

Is this necessary? No. But, it sure is convenient.

Another thing to be aware of: in Python 2, variables in list comprehensions are leaked as well:

>>> [x**2 for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> x
9

But, the same does not apply to Python 3.

Solution 4 - Python

One of the primary influences for Python is ABC, a language developed in the Netherlands for teaching programming concepts to beginners. Python's creator, Guido van Rossum, worked on ABC for several years in the 1980s. I know almost nothing about ABC, but as it is intended for beginners, I suppose it must have a limited number of scopes, much like early BASICs.

Solution 5 - Python

It is a design choice in Python, which often makes some tasks easier than in other languages with the typical block scope behavior.

But oftentimes you would still miss the typical block scopes, because, say, you might have large temporary arrays which should be freed as soon as possible. It could be done by temporary function/class tricks but still there is a neater solution achieved with directly manipulating the interpreter state.

from scoping import scoping
a = 2 

with scoping():
    assert(2 == a)
    a = 3
    b = 4
    scoping.keep('b')
    assert(3 == a) 

assert(2 == a) 
assert(4 == b)

https://github.com/l74d/scoping

Solution 6 - Python

If you have a break statement in the loop (and want to use the iteration value later, perhaps to pick back up, index something, or give status), it saves you one line of code and one assignment, so there's a convenience.

Solution 7 - Python

For starters, if variables were local to loops, those loops would be useless for most real-world programming.

In the current situation:

# Sum the values 0..9
total = 0
for foo in xrange(10):
    total = total + foo
print total

yields 45. Now, consider how assignment works in Python. If loop variables were strictly local:

# Sum the values 0..9?
total = 0
for foo in xrange(10):
    # Create a new integer object with value "total + foo" and bind it to a new
    # loop-local variable named "total".
    total = total + foo
print total

yields 0, because total inside the loop after the assignment is not the same variable as total outside the loop. This would not be optimal or expected behavior.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionchimeracoderView Question on Stackoverflow
Solution 1 - PythonJeremy BrownView Answer on Stackoverflow
Solution 2 - PythonatzzView Answer on Stackoverflow
Solution 3 - PythoncarlView Answer on Stackoverflow
Solution 4 - PythonkindallView Answer on Stackoverflow
Solution 5 - Pythonl74dView Answer on Stackoverflow
Solution 6 - PythonMacView Answer on Stackoverflow
Solution 7 - PythonKirk StrauserView Answer on Stackoverflow