[Python-Dev] 2.5 and beyond

Tim Peters tim.peters at gmail.com
Sat Jul 1 10:08:39 CEST 2006


[Giovanni Bajo]
>> >>> a = []
>> >>> for i in range(10):
>>
>> ...     a.append(lambda: i)
>> ...
>>
>> >>> print [x() for x in a]
>>
>> [9, 9, 9, 9, 9, 9, 9, 9, 9, 9]
>>
>> This subtle semantic of lambda is quite confusing, and still forces people to
>> use the "i=i" trick.

[Greg Ewing]
> This has *nothing* to do with the semantics of lambda!
> It's because Python's for-loop doesn't put its control
> variable in a new scope, the way Scheme's equivalent
> construct does.

I don't think I follow that.  Scheme has no loops in Python's sense --
things like "do" are shorthand for expressing stylized recursion,
where each conceptual iteration gets a fresh set of "loop variables".
When people talk about giving a Python for-loop vrbl its own scope,
they generally don't mean a new scope on _each iteration_, they just
mean that, e.g.,

    i = 5
    for i in range(10):
         # do stuff
    print i

prints 5 intead of 9, about the same as creating a nested block with
its own autos in C.  The Scheme way is more like:

    i = 5
    def step(i):
         # do stuff
         if i < 9:
             step(i+1)
    step(0)
    print i

except with tail-recursion elimination.  That also prints 5, but does
a hell of a lot more than _just_ arrange for that.

> *That's* what needs to be addressed to fix this problem.
> I've made a suggestion about that before, but Guido
> rejected it, so I won't repeat it here.

Don't recall what that was, but creating a new scope on each iteration
sounds hard to explain in Python.  If Giovanni wants the Scheme way
;-), it's available:

"""
a = []
def step(i):
    a.append(lambda: i)
    if i < 9:
        step(i+1)
step(0)
print [x() for x in a]
"""

prints [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], although it's more sanely
written in Python with a loop:

"""
def make_lambda(i):
    return lambda: i
a = []
for i in range(10):
    a.append(make_lambda(i))
print [x() for x in a]
"""

Abusing the default-argument machinery to capture current bindings is
never necessary, and _is_ abuse.  Of course I do it too -- but rarely
:-)


More information about the Python-Dev mailing list