A python example in ipython:

In [1]: for i in range(10):
...:     print "i in loop:", i
i in loop: 0
i in loop: 1
i in loop: 2
i in loop: 3
i in loop: 4
i in loop: 5
i in loop: 6
i in loop: 7
i in loop: 8
i in loop: 9

In [2]: print "i out of loop:", i
i out of loop: 9

This bit me last night while writing some code for a digital communications lab assignment. I typed the wrong variable name, which was from an inner loop when I meant to use the element from the outer loop. Is there actually a sane reason for a loop variable not to go out of scope when the loop ends? Tell me there's a good reason for it. It took me completely by surprise.

comment 1

Python does not have any form of block scope. Local variables are always in scope for an entire function. It may be suprising when coming from almost any other language, but at least once you know it, it's predictable. (JavaScript has the same scoping rule as Python, which is even more surprising because it uses the same block syntax as the other "curly brace" languages.)

It could be worse. Python 2.1 and earlier didn't even have nested lexical scopes. (You could access only global variables and immediate local variables.)

Comment by mbrubeck [limpet.net] Fri 25 Sep 2009 03:40:13 AM UTC
Python has 3 scopes...

From: http://www.network-theory.co.uk/docs/pytut/PythonScopesandNameSpaces.html

At any time during execution, there are at least three nested scopes whose namespaces are directly accessible: the innermost scope, which is searched first, contains the local names; the namespaces of any enclosing functions, which are searched starting with the nearest enclosing scope; the middle scope, searched next, contains the current module's global names; and the outermost scope (searched last) is the namespace containing built-in names.

"i" is created in the local (that is: function local) scope as would be any variables created inside the for loop. For example:

>>> def foo():
...     for i in range(10):
...         if i > 5:
...             q = 1
...     print i
...     print q
>>> foo()

Both i & q are created in the local scope and don't leave scope until foo() returns.

I'm not really sure if this is the best answer, but I hope it helps some. :-)

Comment by schmichael Fri 25 Sep 2009 03:49:59 AM UTC
Use of inner loop variables outside the loop

I do use this feature sometimes, using break. The contrived example:

In [1]:l = ['a', 'b', 'c', 'd']

In [2]:for i, j in enumerate(l):
   .4.:    if j == 'c':
   .4.:        break

In [3]:print l[i]
Comment by bruynooghe [blogspot.com] Fri 25 Sep 2009 08:14:10 AM UTC
comment 4

There is a similar problem with list comprehensions:

>>> [i for i in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> i

.. but at least that one is fixed in Python 3. I really would not recommend relying on this behaviour, bruynooghe.

Comment by lamby Fri 25 Sep 2009 08:59:31 AM UTC
comment 5

Hmm, thanks for the explanations!

I think I am still of the opinion that this is Bad and Wrong, though. I haven't written as much JavaScript as I have python, or perhaps it might have tripped me up there too, mbrubeck.

Comment by christine-spang [myopenid.com] Fri 25 Sep 2009 01:08:13 PM UTC
Loops and scopes??

I'm not a python programmer but in my own experience with other languages it seems loops don't have a scope. And I figure for good reason.

If you are looping through some data (in a search for example) and you find your data, won't you want to keep the reference for later use?

Anyway, I've never seen a scope in any kind of loop I've used (for, while, do, etc.)...

Comment by trelaine [myopenid.com] Fri 25 Sep 2009 03:29:10 PM UTC
comment 7

trelaine: python is doing some magic with iterators here. IMHO, it makes sense that if the language is going to have special constructs to iterate through a list, the iteration variable should disappear when the loop is done, because it's only intended for use within the loop. Usage outside the loop is probably a mistake.

Other languages may not have "loop-scope", but that's probably because in many other languages loops are just blocks, and the language has block scoping.

For example, Perl has a similar construct:

use strict;
use warnings;

for my $var (1..10) {
    print "var in loop: $var\n";

print "var out of loop: $var";

This will die on compilation with:

spang@m12-182-3:~/tmp> perl foo.pl 
Global symbol "$var" requires explicit package name at foo.pl line 9.
Execution of foo.pl aborted due to compilation errors.

Function-level scoping is just weird to me.

Comment by christine-spang [myopenid.com] Fri 25 Sep 2009 04:35:19 PM UTC
comment 8
This catches me out all the time :-(
Comment by dannipenguin [livejournal.com] Fri 25 Sep 2009 11:52:24 PM UTC
python can't have block scope

In python, assigning a variable creates it in in the innermost scope the assignment took place in. If it had block scope, than variable assignments in block would only be valid in that block, which is generally not what you want. Therefore python can't have block scope.

The iterator variable remaining in existence after the block is somewhat unfortunate side-effect, but one that can't be fixed without introducing explicit variable declarations. And not needing those is one of best things in python.

Comment by bulb [drak.ucw.cz] Wed 30 Sep 2009 06:53:48 AM UTC