function object and copies of variables

S

stan

can a function object store values so this 3 liner can print 0 1 instead
of 1 1?

f = []
for i in [0, 1]: f.append(lambda: i)
for j in f: print j()


TIA
 
D

Diez B. Roggisch

f = []
for i in [0, 1]: f.append(lambda a = i: a)
for j in f: print j()
 
S

Steven Bethard

can a function object store values so this 3 liner can print 0 1 instead
of 1 1?

f = []
for i in [0, 1]: f.append(lambda: i)
for j in f: print j()

Just thought I might give a little extra commentary on why Diez B. Roggisch's
solution works. First, I'm going to write it in a more readable (IMHO) form[1]:
.... def func(i=i):
.... return i
.... f.append(func)
.... .... print j()
....
0
1

Now, the problem you're running into here is that def (and lambda) in Python
don't bind names in their body to values at the time at which the function is
defined. If they did, the following would be invalid:
.... def g():
.... print j
.... j = 1
.... g()
.... 1

because the name 'j' is not bound to a value until after g is defined. Instead,
what happens is that the g() function doesn't try to look up a value for j until
it reaches the statement 'print j'. Since g() is called after the name 'j' is
bound to the value 1, when g() does finally look up a value for j, it is able to
find one in the f function's scope.

Back to your original question, what happens when you write:
.... def func():
.... return i
.... f.append(func)
.... .... print j()
....
1
1

(or the equivalent) is that the only i available to func is the one from the for
loop, and this one has the value 1:
.... pass
.... 1

So, when func() is called by calling j(), func looks for i, finds it in the
global scope, and uses that value, which is 1 both times that func() is called,
because it is called after the end of the first for loop. Contrast this with:
.... def func():
.... return i
.... print func()
....
0
1

where the for-loop has not completed, and so i still has the value 0 the first
time func() is called.

Diez B. Roggisch's solution solves your problem by binding i to a value at the
time that func is defined, taking advantage of the fact that default argument
values are evaluated and bound at function definition time.

Hope this is helpful.

Steve

[1] If you're really looking for the fewer lines, try:

for j in [lambda i=i: i for i in (0, 1)]:
print j()

Not only is it more readable (IMHO), it's also probably faster since list
comprehensions are almost always faster than a for-loop with an append.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,210
Messages
2,571,091
Members
47,691
Latest member
Jenny-jane

Latest Threads

Top