list of lambda

Discussion in 'Python' started by jena, Nov 11, 2005.

  1. jena

    jena Guest

    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'

    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[0]()=='A'

    it is OK or it is bug?
    can i do it correctly simplier than with helper X class?

    thanks
    Honza Prochazka
     
    jena, Nov 11, 2005
    #1
    1. Advertising

  2. 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'


    Fredrik Lundh provided the general solution, but in this specific case,
    the simplest solution is:

    l = [x.upper for x in ['a', 'b', 'c']]
     
    Leif K-Brooks, Nov 12, 2005
    #2
    1. Advertising

  3. jena

    Paul Rubin Guest

    jena <> writes:
    > l=[lambda:x.upper() for x in ['a','b','c']]
    > then l[0]() 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']]
     
    Paul Rubin, Nov 12, 2005
    #3
  4. jena

    Guest

    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[0]() 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 12, 2005
    #4
  5. 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.


    --
    Steven.
     
    Steven D'Aprano, Nov 12, 2005
    #5
  6. On 11 Nov 2005 18:28:22 -0800, Paul Rubin <http://> wrote:

    >jena <> writes:
    >> l=[lambda:x.upper() for x in ['a','b','c']]
    >> then l[0]() 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']]


    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[0],'=?=',lamb()

    ...
    A =?= A
    B =?= B
    C =?= C

    Regards,
    Bengt Richter
     
    Bengt Richter, Nov 12, 2005
    #6
  7. 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[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
     
    Bengt Richter, Nov 12, 2005
    #7
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Peter Barth

    Mix lambda and list comprehension?

    Peter Barth, Jul 15, 2003, in forum: Python
    Replies:
    4
    Views:
    412
    Michele Simionato
    Jul 17, 2003
  2. Roman Suzi
    Replies:
    13
    Views:
    608
    Bengt Richter
    Jan 7, 2005
  3. Max Rybinsky
    Replies:
    5
    Views:
    313
    Max Rybinsky
    Oct 30, 2005
  4. Steve Dogers

    lambda vs non-lambda proc

    Steve Dogers, Mar 30, 2009, in forum: Ruby
    Replies:
    1
    Views:
    178
    Sean O'Halpin
    Mar 30, 2009
  5. Haochen Xie
    Replies:
    4
    Views:
    245
    Haochen Xie
    Mar 17, 2013
Loading...

Share This Page