Skip to main content.
April 18th, 2007

misteries of python’s lambda function

I have a Python’s cycle into which I would like to make lambdas:

s = {}
for x in ['stupid', 'genial']:
s[x] = lambda prefix: "%s %s" % (prefix, x)


print s['stupid']('I am')
print s['genial']('You are')

Would you expect this output?

I am stupid
You are genial

No, no. This is the real output:

I am genial
You are genial

Even if I’m happy to be genial too, I wonder what happened. The reply is simple: python’s lambda binding. The x variables isn’t bound when the lambda is constructed, but when the lambda is called. Since both are called only at the end of the cycle, its value is the last value of x, which is 'genial'. Ok, now that we’ve understood what had happened, how can I solve this problem?

If you’re lucky, you’re using Python 2.5 or higher. In this case, they invented functools, and functools.partial:

from functools import partial

s = {}
for x in ['stupid', 'genial']:
  s[x] = partial(lambda prefix, xx: "%s %s" % (prefix, xx), xx = x)

print s['stupid']('I am')
print s['genial']('You are')

Now the lambda takes two arguments, one of which is bound, forceably, to the current value of x. Ok, this solution is not so elegant, but at least is a one-liner. And if you have a lower version of Python? Have fun in decyphering the following one:

s = {}
for x in ['stupid', 'genial']:
  def aux():
    v = x
    return lambda prefix: "%s %s" % (prefix, v)
  s[x] = aux()

print s['stupid']('I am')
print s['genial']('You are')

It works on a simple trick: the aux function put the too inconstant variable x in a local variable, v, then use that one. Since a new local variable is allocated every time, the value of x is saved in that local variable. It is thrown away, once the function exit, but the lambda-binding save the variable, which we can safely get afterwards.

I quite love Python, but, really… some things are really stupid!

Posted by mattia as functools, lambda, python at 2:01 PM CEST

No Comments »