Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a Pythonic way to close over a loop variable?

I just ran across Eric Lippert's Closing over the loop variable considered harmful via SO, and, after experimenting, realized that the same problem exists (and is even harder to get around) in Python.

>>> l = []
>>> for r in range(10):
...   def foo():
...      return r
...   l.append(foo)
...
>>> for f in l:
...   f()
...
9
9
9
# etc

and, the standard C# workaround doesn't work (I assume because of the nature of references in Python)

>>> l = []
>>> for r in range(10):
...   r2 = r
...   def foo():
...      return r2
...   l.append(foo)
...
>>> for f in l:
...   f()
...
9
9
9
# etc

I recognize that this isn't much of a problem in Python with its general emphasis on non-closure object structures, but I'm curious if there is an obvious Pythonic way to handle this, or do we have to go the JS route of nested function calls to create actually new vars?

>>> l = []
>>> for r in range(10):
...     l.append((lambda x: lambda: x)(r))
...
>>> for f in l:
...     f()
...
0
1
2
# etc
like image 229
quodlibetor Avatar asked Jan 20 '12 19:01

quodlibetor


People also ask

How do you end a for loop in Python?

In Python, the break statement provides you with the opportunity to exit out of a loop when an external condition is triggered. You'll put the break statement within the block of code under your loop statement, usually after a conditional if statement.

How do you stop a loop when a condition is met Python?

Python provides two keywords that terminate a loop iteration prematurely: The Python break statement immediately terminates a loop entirely. Program execution proceeds to the first statement following the loop body. The Python continue statement immediately terminates the current loop iteration.

What can I use instead of a break in Python?

Continue is also a loop control statement just like the break statement. continue statement is opposite to that of break statement, instead of terminating the loop, it forces to execute the next iteration of the loop. As the name suggests the continue statement forces the loop to continue or execute the next iteration.


1 Answers

One way is to use a parameter with default value:

l = []
for r in range(10):
    def foo(r = r):
        return r
    l.append(foo)

for f in l:
    print(f())

yields

0
1
2
3
4
5
6
7
8
9

This works because it defines an r in foo's local scope, and binds the default value to it at the time foo is defined.


Another way is to use a function factory:

l = []
for r in range(10):
    def make_foo(r):
        def foo():
            return r
        return foo
    l.append(make_foo(r))

for f in l:
    print(f())

This works because it defines r in make_foo's local scope, and binds a value to it when make_foo(r) is called. Later, when f() is called, r is looked-up using the LEGB rule. Although r is not found in the local scope of foo, it is found in the enclosing scope of make_foo.

like image 188
unutbu Avatar answered Oct 01 '22 19:10

unutbu