445,885 Members | 1,499 Online Need help? Post your question and get tips & solutions from a community of 445,885 IT Pros & Developers. It's quick & easy.

# list of lambda

 P: n/a hello, when i create list of lambdas: l=[lambda:x.upper() for x in ['a','b','c']] then l() returns 'C', i think, it should be 'A' my workaround is to define helper class with __call__ method: class X: def __init__(self,s): self.s=s def __call__(self): return self.s.upper() l=[X(x) for x in ['a','b','c']] now it is correct, l()=='A' it is OK or it is bug? can i do it correctly simplier than with helper X class? thanks Honza Prochazka Nov 11 '05 #1
6 Replies

 P: n/a jena wrote: hello, when i create list of lambdas: l=[lambda:x.upper() for x in ['a','b','c']] then l() returns 'C', i think, it should be 'A' Fredrik Lundh provided the general solution, but in this specific case, the simplest solution is: l = [x.upper for x in ['a', 'b', 'c']] Nov 22 '05 #2

 P: n/a jena writes: l=[lambda:x.upper() for x in ['a','b','c']] then l() returns 'C', i think, it should be 'A' Yeah, this is Python late binding, a standard thing to get confused over. You want: l = [lambda x=x: x.upper() for x in ['a', 'b', 'c']] Nov 22 '05 #3

 P: n/a Leif K-Brooks wrote: jena wrote: hello, when i create list of lambdas: l=[lambda:x.upper() for x in ['a','b','c']] then l() returns 'C', i think, it should be 'A' Fredrik Lundh provided the general solution, but in this specific case, the simplest solution is: l = [x.upper for x in ['a', 'b', 'c']] how about : l = ['A','B','C'] Nov 22 '05 #4

 P: n/a 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() 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 [ at 0xf6ff9844>, at 0xf6ff987c>, at 0xf6ff98b4>] Why do you want a list of functions? L() 'C' L() 'C' L() '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() 'IT IS WAS A MISTAKE TO EXPOSE LIST COMPREHENSION VARIABLES' py> L() '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 [, , ] py> L(); L(); L() '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 [, , ] py> L(); L(); L() 'A' 'B' 'C' Hope this helps. -- Steven. Nov 22 '05 #5

 P: n/a On 11 Nov 2005 18:28:22 -0800, Paul Rubin wrote: jena writes: l=[lambda:x.upper() for x in ['a','b','c']] then l() returns 'C', i think, it should be 'A'Yeah, this is Python late binding, a standard thing to get confusedover. You want: l = [lambda x=x: x.upper() for x in ['a', 'b', 'c']] or if you want the upper() eagerly (and do it only once each, in case of multiple lambda calls) l = [lambda x=x.upper():x for x in ['a', 'b', 'c']] l = [lambda x=x.upper():x for x in ['a', 'b', 'c']] for lamb in l: print lamb.func_defaults,'=?=',lamb() ... A =?= A B =?= B C =?= C Regards, Bengt Richter Nov 22 '05 #6

 P: n/a On Sat, 12 Nov 2005 14:55:31 +1100, Steven D'Aprano 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() 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[ at 0xf6ff9844>, at 0xf6ff987c>, at 0xf6ff98b4>]Why do you want a list of functions? L()'C' L()'C' L()'C'What you have discovered is a bug in your code, caused by some accidentalbehaviour 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 comprehensionis exposed. It shouldn't be, and soon won't be -- it will be an error torefer 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()'IT IS WAS A MISTAKE TO EXPOSE LIST COMPREHENSION VARIABLES'py> L()'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 justthe 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 toupper case.lambda x: x.upper # note the brackets are goneis an anonymous function which takes input x and returns a function(technically, a method) which will return x converted to upper case whencalled.So the list comprehension you want is: # note all the bracketspy> L = [(lambda x: x.upper)(x) for x in ['a', 'b', 'c']]py> L[, , ]py> L(); L(); L()'A''B''C'But now that gives us a clue that using lambda just adds too muchcomplication! What we want is the string methods, and we don't need lambdato get them. So we can make it much simpler:py> L = [x.upper for x in ['a', 'b', 'c']]py> L[, , ]py> L(); L(); L()'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 'a'.upper.__self__ 'a' str.upper That's the unbound method. If we bind it to 'a' in the usual way behind inst.method, type('a') type('a').__dict__ type('a').__dict__['upper'] type('a').__dict__['upper'].__get__('a', type('a')) Or str.upper.__get__('a', str) we get the bound method. So the clue is "... of str objects" vs ".. of str object at ..." Maybe nicer would be Same if it's inherited: class S(str): pass ... S('a').upper 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 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 Nov 22 '05 #7

### This discussion thread is closed

Replies have been disabled for this discussion. 