list of lambda

Bengt Richter bokr at oz.net
Sat Nov 12 00:22:18 EST 2005


On Sat, 12 Nov 2005 14:55:31 +1100, Steven D'Aprano <steve at REMOVETHIScyber.com.au> wrote:

>On Sat, 12 Nov 2005 00:17:59 +0100, jena wrote:
>
>> hello,
>> when i create list of lambdas:
>> l=[lambda:x.upper() for x in ['a','b','c']]
>> then l[0]() returns 'C', i think, it should be 'A'
>
>What is wrong with just doing this?
>
>L = [x.upper() for x in ['a', 'b', 'c']]
>
>
>
>py> L = [lambda: x.upper() for x in ['a', 'b', 'c']]
>py> L
>[<function <lambda> at 0xf6ff9844>, <function <lambda> at 0xf6ff987c>, <function <lambda> at 0xf6ff98b4>]
>
>Why do you want a list of functions?
>
>>>> L[0]()
>'C'
>>>> L[1]()
>'C'
>>>> L[2]()
>'C'
>
>What you have discovered is a bug in your code, caused by some accidental
>behaviour of Python which will be removed in a new version soon:
>
>py> [x.upper() for x in "abc"]
>['A', 'B', 'C']
>py> x
>'c'
>
>You can see that the temporary variable x used by the list comprehension
>is exposed. It shouldn't be, and soon won't be -- it will be an error to
>refer to the list comp variable outside the list comp.
>
>Now watch this:
>
>py> x = "it is was a mistake to expose list comprehension variables"
>py> L[0]()
>'IT IS WAS A MISTAKE TO EXPOSE LIST COMPREHENSION VARIABLES'
>py> L[1]()
>'IT IS WAS A MISTAKE TO EXPOSE LIST COMPREHENSION VARIABLES'
>
>Do you see what is going on now?
>
>
>Assuming you actually do need a list of *functions*, rather than just
>the results of those functions, this would be the way to do it:
>
>lambda x: x.upper()
>
>is an anonymous function which takes input x and returns x converted to
>upper case.
>
>lambda x: x.upper  # note the brackets are gone
>
>is an anonymous function which takes input x and returns a function
>(technically, a method) which will return x converted to upper case when
>called.
>
>So the list comprehension you want is:
>
>
># note all the brackets
>py> L = [(lambda x: x.upper)(x) for x in ['a', 'b', 'c']]
>py> L
>[<built-in method upper of str object at 0xf706a040>, <built-in method upper of str object at 0xf706a0e0>, <built-in method upper of str object at 0xf706ca00>]
>py> L[0](); L[1](); L[2]() 
>'A'
>'B'
>'C'
>
>But now that gives us a clue that using lambda just adds too much
>complication! What we want is the string methods, and we don't need lambda
>to get them. So we can make it much simpler:
>
>py> L = [x.upper for x in ['a', 'b', 'c']]
>py> L
>[<built-in method upper of str object at 0xf706a040>, <built-in method upper of str object at 0xf706a0e0>, <built-in method upper of str object at 0xf706ca00>]
>py> L[0](); L[1](); L[2]()
>'A'
>'B'
>'C'
>
>Hope this helps.
>
Yes, but it exposes bad (improvable;-) repr text IMO:
It's not just a method, it's a _bound_ method, but the repr text
doesn't say so (unless you read clues between the lines)

 >>> 'a'.upper
 <built-in method upper of str object at 0x02EB03A0>
 >>> 'a'.upper.__self__
 'a'

 >>> str.upper
 <method 'upper' of 'str' objects>

That's the unbound method. If we bind it to 'a' in the usual way
behind inst.method,

 >>> type('a')
 <type 'str'>
 >>> type('a').__dict__
 <dictproxy object at 0x02E81C44>
 >>> type('a').__dict__['upper']
 <method 'upper' of 'str' objects>
 >>> type('a').__dict__['upper'].__get__('a', type('a'))
 <built-in method upper of str object at 0x02EB03A0>

Or
 >>> str.upper.__get__('a', str)
 <built-in method upper of str object at 0x02EB03A0>

we get the bound method. So the clue is "... of str objects" vs ".. of str object at ..."
Maybe nicer would be
 <bound built-in method upper of str object at 0x02EB03A0>

Same if it's inherited:
 >>> class S(str): pass
 ...
 >>> S('a').upper
 <built-in method upper of S object at 0x02F87A7C>
 >>> S('a').upper()
 'A'

But if we override, we get 'bound method ...'

 >>> class S(str):
 ...     def upper(self): return 'S.upper => %r' % str.upper(self)
 ...
 >>> S('a').upper
 <bound method S.upper of 'a'>
 >>> S('a').upper()
 "S.upper => 'A'"

A nit. I thought it clever to replace the lambda with the the bound method,
but while supplying a callable, it still postpones the upper execution, and
will repeat it for each call, whereas lambda x=x.upper():x does the work once
up front (in general not always possible, of course).

Regards,
Bengt Richter



More information about the Python-list mailing list