202

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.

chimeracoder
  • 17,532
  • 20
  • 57
  • 59
  • 10
    If you don't want the `for` loop cluttering your global namespace, wrap it in a function. Closures galore! – jathanism Aug 31 '10 at 20:32
  • 25
    Unless you're running a loop in the global namespace (uncommon), it's cluttering a *local* namespace. – Glenn Maynard Aug 31 '10 at 20:45
  • 4
    If this didn't exist, how would you continue processing later at the point you left off inside the loop? Just define the control variable *before* the loop? – endolith Aug 31 '12 at 00:45
  • 11
    @endolith Yeah... Why not require that? – Steven Lu Jun 06 '13 at 16:36
  • 1
    @StevenLu: because that would be more work for little to no benefit? – endolith Jun 06 '13 at 18:08
  • 5
    well people are just gonna prefer what they're used to doing. I'd say this sort of thing hurts the python coder who gets used to this sort of thing and has to go through a painful process when switching to a different language. For the rest of us, it's a neat little shortcut I suppose. – Steven Lu Jun 06 '13 at 18:23
  • 2
    @StevenLu: So we shouldn't make nice languages, because then the pain of switching to crappier languages is even more apparent? :) – endolith Jun 06 '13 at 18:49
  • No, not having to init these variables makes python better and most would be inclined to agree. gotta weigh the options – Steven Lu Jun 06 '13 at 23:21
  • 2
    This scares the hell out of me for a language which otherwise uses white space as an "elegant" way of managing scope. It makes me question whether my any of my "scoped" variables are truly in their own scope. – David Aug 17 '17 at 23:23
  • Is this good memory-wise? And if Python does eventually get rid of them, is it safe to use such variables throughout the program? – User 10482 Mar 08 '20 at 13:55
  • I have to use python after 30+ years of c and c++. It seems to me that using a lot of small functions (e.g. a function per loop) is the only way to avoid maintenance headaches that can arise from scoping rules. – zzz777 Oct 25 '20 at 14:34
  • 1
    This cost me in actual damages for a project I did with Jupyter, which had a line like `for y in ys: plot(X, y)` where `y` overrode a preceding label variable that was used further down – Layman Nov 23 '20 at 19:16
  • @endolith can you please elaborate, how can I continue processing later at the point you left off inside the loop? I want to use the index left off after coming out of the loop. – Shubham Agrawal Mar 02 '21 at 07:48
  • @ShubhamAgrawal So just use the index after coming out of the loop – endolith Mar 03 '21 at 05:57
  • Sorry @endolith actually i just realised that i was expecting work of conditional loop from for loop that should be done with while loop in python unlike java – Shubham Agrawal Mar 03 '21 at 12:22

6 Answers6

123

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

Hans Ginzel
  • 5,777
  • 2
  • 20
  • 21
Jeremy Brown
  • 16,210
  • 3
  • 32
  • 27
  • 2
    How would the grammar be more complicated if the scope of the induction variable were limited to the body of the loop? Such a change would be confined to the semantic analysis in Python, not to its grammar. – Charles Apr 28 '15 at 08:50
  • 7
    Loops are not blocks in Python. This sort of behavioral change would call for either changing the grammar fundamentally or providing a special case. The whole concept of an induction variable is also not expressed in the current grammar. The grammar provides the contract for how the interpreter will interpret. My point is that I cannot foresee how a change in this behavior can be done without making the grammar more complicated. It's all moot since the side effect of the design decision has become a feature. – Jeremy Brown Apr 28 '15 at 21:51
  • 1
    This post here https://mail.python.org/pipermail/python-dev/2005-September/056677.html gives more details regarding speed and complication which Mr. Brown alludes to. – rajesh Dec 06 '19 at 06:29
  • This also helped me considerably with the concept: https://eli.thegreenplace.net/2015/the-scope-of-index-variables-in-pythons-for-loops/ – LabGecko Nov 06 '20 at 13:11
66

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

atzz
  • 15,746
  • 3
  • 33
  • 32
  • 4
    I'm confused - what prevents Python from scoping for loops the same way that functions are scoped? – chimeracoder Aug 31 '10 at 18:15
  • 36
    Not really true, it's just that the grammar doesn't go block-crazy. (http://docs.python.org/reference/executionmodel.html#naming-and-binding) "A block is a piece of Python program text that is executed as a unit. The following are blocks: a module, a function body, and a class definition..." – Jeremy Brown Aug 31 '10 at 18:16
  • 2
    @thebackhand, nothing. It was just deemed unnecessary. – habnabit Aug 31 '10 at 18:19
  • 6
    @thebackhand - in languages with blocks, scoping `for` loops is a natural extension of a general principle. In Python it would have to be a special case, and special cases are to be avoided unless they have compelling benefits. – atzz Aug 31 '10 at 21:50
44

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.

LabGecko
  • 179
  • 9
carl
  • 47,134
  • 17
  • 71
  • 81
  • 4
    You could have done that presumably in the `else` clause, ie. `else: print "I did something {0} times".format(count)` - before local scope (that does not exist in Python) disappears – Nas Banov Sep 01 '10 at 00:11
  • 3
    Only the second example doesn't work in Python 3, right? The first still does? Notes as to why it was removed from Python 3? – endolith Aug 31 '12 at 00:44
  • 7
    **for count, item in enumerate(a, start=1):** # default index is from zero – Tao Zhang Aug 31 '16 at 21:05
  • 3
    The first example, rather than being a good use case, seems more like the evidence that this scoping rule is dangerous and should not be relied upon. What if `someiterator` is empty? – max Oct 08 '16 at 17:06
  • 1
    @Nas While an `else` clause could be used in this case, it wouldn't work in general since the loop body could `break` prematurely. – jamesdlin Dec 14 '16 at 00:29
  • Meh. There's an off-by-one error in the first example. – Eric Duminil Jan 25 '18 at 09:32
1

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.

Jay Soyer
  • 6,625
  • 8
  • 47
  • 77
kindall
  • 158,047
  • 31
  • 244
  • 289
1

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.

Mac
  • 19
  • 1
-2

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.

Ben Millwood
  • 6,450
  • 21
  • 44
Kirk Strauser
  • 27,753
  • 5
  • 45
  • 62
  • 5
    Not answering the question. The OP was asking about foo, not total (or bar in their example). – James Bradbury Oct 30 '13 at 10:42
  • 6
    @JamesBradbury `total` and `foo` would still have loop-local bindings in the OP's scenario and the logic is the same. – Kirk Strauser Oct 30 '13 at 14:49
  • 2
    OP: "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." (emphasis mine) – James Bradbury Oct 30 '13 at 16:11
  • 2
    @JamesBradbury You might be right, but I answered this three years ago and it's probably not worth debating now. – Kirk Strauser Oct 30 '13 at 20:56