Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #105840 > unrolled thread

Why lambda in loop requires default?

Started bygvim <gvimrc@gmail.com>
First post2016-03-27 02:46 +0100
Last post2016-03-27 08:29 -0700
Articles 4 — 3 participants

Back to article view | Back to comp.lang.python


Contents

  Why lambda in loop requires default? gvim <gvimrc@gmail.com> - 2016-03-27 02:46 +0100
    Re: Why lambda in loop requires default? Jussi Piitulainen <jussi.piitulainen@helsinki.fi> - 2016-03-27 17:16 +0300
    Re: Why lambda in loop requires default? Jussi Piitulainen <jussi.piitulainen@helsinki.fi> - 2016-03-27 18:15 +0300
    Re: Why lambda in loop requires default? Ned Batchelder <ned@nedbatchelder.com> - 2016-03-27 08:29 -0700

#105840 — Why lambda in loop requires default?

Fromgvim <gvimrc@gmail.com>
Date2016-03-27 02:46 +0100
SubjectWhy lambda in loop requires default?
Message-ID<mailman.80.1459086902.28225.python-list@python.org>
Given that Python, like Ruby, is an object-oriented language why doesn't 
this:

def m():
   a = []
   for i in range(3): a.append(lambda: i)
   return a

b = m()
for n in range(3): print(b[n]())  # =>  2  2  2

... work the same as this in Ruby:

def m
   a = []
   (0..2).each {|i| a << ->(){i}}
   a
end

aa = m
(0..2).each {|n| puts aa[n].()}  # =>  0  1  2


lambda i=i: i

... is needed to make it work in Python. Just wondered why?

gvim

[toc] | [next] | [standalone]


#105844

FromJussi Piitulainen <jussi.piitulainen@helsinki.fi>
Date2016-03-27 17:16 +0300
Message-ID<lf5k2kouhu0.fsf@ling.helsinki.fi>
In reply to#105840
gvim writes:

> Given that Python, like Ruby, is an object-oriented language why
> doesn't this:
>
> def m():
>   a = []
>   for i in range(3): a.append(lambda: i)
>   return a
>
> b = m()
> for n in range(3): print(b[n]())  # =>  2  2  2

I'm going to suggest two variations that may or may not work for you,
with very brief glosses. Just ignore them if you don't see their
relevance.

First, consider:

def w():
  a = []
  for i in range(3): a.append(lambda: i)
  i = "!"
  return a

b = w()
for f in b: print(f()) # => ! ! !

(Those functions depend on the i in the loop.)

> lambda i=i: i
>
> ... is needed to make it work in Python. Just wondered why?

And second, consider: lambda x=i: x

(Those functions are independent of the i in the loop.)

[toc] | [prev] | [next] | [standalone]


#105851

FromJussi Piitulainen <jussi.piitulainen@helsinki.fi>
Date2016-03-27 18:15 +0300
Message-ID<lf5bn60uf2o.fsf@ling.helsinki.fi>
In reply to#105840
gvim writes:

> Given that Python, like Ruby, is an object-oriented language why
> doesn't this:
>
> def m():
>   a = []
>   for i in range(3): a.append(lambda: i)
>   return a
>
> b = m()
> for n in range(3): print(b[n]())  # =>  2  2  2
>
> ... work the same as this in Ruby:
>
> def m
>   a = []
>   (0..2).each {|i| a << ->(){i}}
>   a
> end
>
> aa = m
> (0..2).each {|n| puts aa[n].()}  # =>  0  1  2

No, I'm not taking this to private correspondence, and *I'm* not going
to talk about why Python "couldn't" do certain things differently when
other languages apparently can.

I don't know Ruby. It looks gibberish to me, but my *guess* is that the
following way to make Python give you what you want is not "fiddling" at
all but the exact *same* thing that Ruby makes you do.

fs = [ (lambda i: (lambda : i))(i) for i in range(3) ]
print([ f() for f in fs ]) # => [0, 1, 2]

The following is the same as above. I find it preferable.

fs = [ (lambda k: (lambda : k))(i) for i in range(3) ]
print([ f() for f in fs ]) # => [0, 1, 2]

And the following may be even more same as your Ruby thing.

fs = list(map(lambda k: (lambda : k), range(3)))
print([ f() for f in fs ]) # => [0, 1, 2]

So it's not at all like Python can't do this at all.

[toc] | [prev] | [next] | [standalone]


#105852

FromNed Batchelder <ned@nedbatchelder.com>
Date2016-03-27 08:29 -0700
Message-ID<5bd6b719-8b8c-4c1c-8863-0781e2ba3099@googlegroups.com>
In reply to#105840
On Sunday, March 27, 2016 at 9:55:16 AM UTC-4, g vim wrote:
> Given that Python, like Ruby, is an object-oriented language 

It turns out that "object-oriented" means very little, and lots
of languages that are object-oriented will behave differently
from each other, even where object behavior is concerned.

> why doesn't 
> this:
> 
> def m():
>    a = []
>    for i in range(3): a.append(lambda: i)
>    return a
> 
> b = m()
> for n in range(3): print(b[n]())  # =>  2  2  2
> 
> ... work the same as this in Ruby:
> 
> def m
>    a = []
>    (0..2).each {|i| a << ->(){i}}
>    a
> end
> 
> aa = m
> (0..2).each {|n| puts aa[n].()}  # =>  0  1  2
> 

Like Jussi, I don't know Ruby enough to tell you why Ruby *does*
work as you want, but I can help explain why Python doesn't work
as you want.

When you write "lambda: i", you are creating a closure, because
i is a name from outside the lambda.  In that case, the function
you create with the lambda refers to the variable i, not to the
value i had when you made the lambda.

Later, when you run the function, it will use the current value
of the variable i.  In this case, you have three functions, all
of which refer to the same i, and when you run them all, i is 2,
so they all produce 2.

> 
> lambda i=i: i
> 
> ... is needed to make it work in Python. Just wondered why?

Here you are using the local i as the default value for the
function parameter i.  Function parameter defaults are evaluated
when the function is defined, and the value is stored as part of
the function.  So all three of your functions store a different
value as the default for i.  When the functions are called, they
each use their distinct value as the default for the missing
argument, and they produce 0, 1, 2.

--Ned.

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web