beginner question

Bengt Richter bokr at oz.net
Tue Mar 26 15:40:15 EST 2002


On Tue, 26 Mar 2002 07:29:33 GMT, "Qiang.JL" <dragon at china.org> wrote:

>
>"Bengt Richter" <bokr at oz.net> wrote in message
>news:a7p5o2$5oe$0 at 216.39.172.122...
>> On Tue, 26 Mar 2002 05:25:28 GMT, "Qiang.JL" <dragon at china.org> wrote:
>>
>> >for this line:
>> >
>> >>>> g=(lambda x: ' '.join(x.split())) or (lambda y: 'second')
>> >
>> I doubt if this expression is doing what you want. It is the same form as
>>
>>      g = a or b
>>
>> which will set g to a, unless a counts as false (e.g., None,'',[],(), or
>zero),
>> in which case g will be set to b irrespective of b's value. So your
>expression
>> is effectively equivalent to
>>
>>     g=(lambda x: ' '.join(x.split()))
>>
>
>sorry english is not my native language, but why equivalent ? are you saying
>the second condition is useless?
>
Yes, the way you have written the expression for g, it will be ignored.

The effect of the "or" in the statement

    g=(lambda x: ' '.join(x.split())) or (lambda y: 'second')

is to say

    Bind the symbol 'g' to the first term on the right hand side that evaluates to true.

In this case, the first term is a function defined by (lambda x: ' '.join(x.split()))
Looking with the interpeter, you can see:

 >>> (lambda x: ' '.join(x.split()))
 <function <lambda> at 0x0079FAF0>
  
So your statement could be broken down to a sequence like:

 >>> a = (lambda x: ' '.join(x.split()))
 >>> b = (lambda y: 'second')
 >>> g = a or b

Looking at the results, g becomes bound to the first term of the or:
 >>> a
 <function <lambda> at 0x007A1D70>
 >>> b
 <function <lambda> at 0x007A1720>
 >>> g
 <function <lambda> at 0x007A1D70>
 >>> g==a
 1

(Note that the a and g locations are identical at 7A1D70 vs the b location at 7A1720).
>
>> If you really don't want to use def and you want to use your original
>> expressions, use another lambda to tie them together, e.g., (OTTOMH,
>> not very well tested ;-)
>>
>>  >>> g = lambda x: (lambda x: ' '.join(x.split()))(str(x)) or (lambda y:
>'second')(str(x))
>>  >>> g('   ')
>>  'second'
>>  >>> g(g)
>>  '<function <lambda> at 0x007DB060>'
>>  >>> g(' a     bc    def')
>>  'a bc def'
>>
>> Notice that the ouside lambda has a body that both defines _and_ calls the
>internal lambdas
>> with the str()-protected arguments. The second lambda will only be called
>if the first one
>> returns ''. The outside lambda then returns the result from whichever
>internal one.
>>
>> But I'd say that's pretty ugly to read compared to
>>
>>  >>> def g(x):
>>  ...     return ' '.join(str(x).split()) or 'second'
>>  ...
>>
>> ;-)
>>
>> Regards,
>> Bengt Richter
>>
>
>if the original lambdas work like yours why you add another one outside ? it
>seems does the same thing.
Well, it only seems ;-) Look closely at the way it's written (see below).

>Weird i run into problem with this one ..
>
>>>> def g(x):
> return ' '.join(str(x).split()) or 'second'
>
>>>> g(0)
>'0'
>>>> g('')
>'second'
>>>> g=(lambda x: ' '.join(str(x).split())) or (lambda x: 'second')
>>>> g('')
>''            # why it prints '' instead of 'second' ??????
See above. You are still misunderstanding the meaning of your

   g=(lambda x: ' '.join(str(x).split())) or (lambda x: 'second')

statement. It does _NOT_ combine the two lambdas into a new function.
Instead, it chooses ONE of the lambdas according to the 'or'.

That is why I put another lambda around them both. So that the
outside lambda could use the 'or' to choose between their _results_.

Notice that the body of the ouside lambda is an expression that
actually _calls_ the inside lambdas:

   (lambda x: ' '.join(x.split()))(str(x)) or (lambda y:'second')(str(x))
                                  ^^^^^^^^                       ^^^^^^^^
so the 'or' is choosing between results, not between lambdas. To make a
function out of the above expression requires the outside lambda.

>>>> g(0)
>'0'
>
>what's wrong here ?

Nothing. Everything is working as written ;-)

When the result of a complex expression seems mysterious, try evaluating
smaller pieces interactively. Also, if you play with the disassembler, you
can see how the compiler translates your statements and expressions. E.g.,
comparing your g definition with my wrapped one:

 >>> import dis
 >>> dis.dis(compile("""g=(lambda x: ' '.join(str(x).split())) or (lambda x: 'second')""",'','exec'))
           0 SET_LINENO               0 

           3 SET_LINENO               1
           6 LOAD_CONST               0 (<code object <lambda> at 007DAB90, file "", line>)
           9 MAKE_FUNCTION            0
          12 JUMP_IF_TRUE             7 (to 22)
             ^^^^^^^^^^^^-- here it's choosing the first lambda and jumping to 22 to store it
                            because the value being tested here is the lambda function itself,
                            not a result of calling the lambda, as in the correct version --
          15 POP_TOP
          16 LOAD_CONST               1 (<code object <lambda> at 007DAB10, file "", line >)
          19 MAKE_FUNCTION            0
     >>   22 STORE_NAME               0 (g)
          25 LOAD_CONST               2 (None)
          28 RETURN_VALUE
 >>>

Versus:

 >>> dis.dis(compile("""g = lambda x: (lambda x: ' '.join(x.split()))(str(x)) or (lambda y:'second'
 str(x))""",'','exec'))
           0 SET_LINENO               0

           3 SET_LINENO               1
           6 LOAD_CONST               0 (<code object <lambda> at 007DB3A0, file "", line 1>)
           9 MAKE_FUNCTION            0
          12 STORE_NAME               0 (g)
          15 LOAD_CONST               1 (None)
          18 RETURN_VALUE

Here it only sees one lambda. It is not choosing between two to store with name 'g'. To see how
the body expression of this lambda looks, we can disassemble it. Note that this is an expression,
not a statement, so we have to tell the compiler 'eval' instead of 'exec':

 >>> dis.dis(compile("""(lambda x: ' '.join(x.split()))(str(x)) or (lambda y:'second')(str(x))""",'eval'))
           0 SET_LINENO               0
           3 LOAD_CONST               0 (<code object <lambda> at 007D9130, file "", line 1>)
           6 MAKE_FUNCTION            0
             ^^^^^^^^^^^^^-- here we have the first lambda, but we're NOT going to JUMP_IF_TRUE here
           9 LOAD_NAME                0 (str)
          12 LOAD_NAME                1 (x)
          15 CALL_FUNCTION            1
             ^^^^^^^^^^^^^-- here we call str(x) for safety to make the arg for the lambda
          18 CALL_FUNCTION            1
             ^^^^^^^^^^^^^-- here we call the first lambda,, and now have the result '' or 'something'
          21 JUMP_IF_TRUE            19 (to 43)
             ^^^^^^^^^^^^ -- this will not jump if the result was ''. Here we may go on to the second lamda
          24 POP_TOP
          25 LOAD_CONST               1 (<code object <lambda> at 007D91C0, file "", line 1>)
          28 MAKE_FUNCTION            0
             ^^^^^^^^^^^^^-- here we have the second lambda
          31 LOAD_NAME                0 (str)
          34 LOAD_NAME                1 (x)
          37 CALL_FUNCTION            1
             ^^^^^^^^^^^^^-- call str(x) for safety as before
          40 CALL_FUNCTION            1
             ^^^^^^^^^^^^^-- here we call the second lambda
     >>   43 RETURN_VALUE

For the code of the lambda bodies:
 >>> dis.dis(compile("""' '.join(x.split())""",'','eval'))
           0 SET_LINENO               0
           3 LOAD_CONST               0 (' ')
           6 LOAD_ATTR                0 (join)
           9 LOAD_NAME                1 (x)
          12 LOAD_ATTR                2 (split)
          15 CALL_FUNCTION            0
          18 CALL_FUNCTION            1
          21 RETURN_VALUE
and
 >>> dis.dis(compile("""'second'""",'','eval'))
           0 SET_LINENO               0
           3 LOAD_CONST               0 ('second')
           6 RETURN_VALUE

HTH. BTW, why did you choose lambda to define your g function? lambdas in Python
are limited to a single expression body, so it is very constraining, and you
wind up writing hard-to-read code if you need to do anything complex.

Immediately binding to a symbol like your 'g' just begs for using def instead.

Regards,
Bengt Richter




More information about the Python-list mailing list