Why do closures do this?

J

John O'Hagan

Somewhat apropos of the recent "function principle" thread, I was recently surprised by this:

funcs=[]
for n in range(3):
def f():
return n
funcs.append(f)

[i() for i in funcs]

The last expression, IMO surprisingly, is [2,2,2], not [0,1,2]. Google tells me I'm not the only one surprised, but explains that it's because "n" in the function "f" refers to whatever "n" is currently bound to, not what it was bound to at definition time (if I've got that right), and that there are at least two ways around it: either make a factory function:

def mkfnc(n):
def fnc():
return n
return fnc

funcs=[]
for n in range(3):
funcs.append(mkfnc(n))

which seems roundabout, or take advantage of the "default values set at definition time" behaviour:

funcs=[]
for n in range(3):
def f(n=n):
return n
funcs.append(f)

which seems obscure, and a side-effect.

My question is, is this an inescapable consequence of using closures, or is it by design, and if so, what are some examples of where this would be the preferred behaviour?

Regards,

John
 
C

Carl Banks

Somewhat apropos of the recent "function principle" thread, I was recently surprised by this:

funcs=[]
for n in range(3):
def f():
return n
funcs.append(f)

[i() for i in funcs]

The last expression, IMO surprisingly, is [2,2,2], not [0,1,2]. Google tells me I'm not the only one surprised, but explains that it's because "n" in the function "f" refers to whatever "n" is currently bound to, not what it was bound to at definition time (if I've got that right), and that there are at least two ways around it: ....
My question is, is this an inescapable consequence of using closures, or is it by design, and if so, what are some examples of where this would be the preferred behaviour?


It is the preferred behavior for the following case.

def foo():
def printlocals():
print a,b,c,d
a = 1; b = 4; c = 5; d = 0.1
printlocals()
a = 2
printlocals()

When seeing a nested function, there are strong expectations by most peoplethat it will behave this way (not to mention it's a lot more useful). It's only for the less common and much more advanced case of creating a closure in a loop that the other behavior would be preferred.


Carl Banks
 
C

Carl Banks

Somewhat apropos of the recent "function principle" thread, I was recently surprised by this:

funcs=[]
for n in range(3):
def f():
return n
funcs.append(f)

[i() for i in funcs]

The last expression, IMO surprisingly, is [2,2,2], not [0,1,2]. Google tells me I'm not the only one surprised, but explains that it's because "n" in the function "f" refers to whatever "n" is currently bound to, not what it was bound to at definition time (if I've got that right), and that there are at least two ways around it: ....
My question is, is this an inescapable consequence of using closures, or is it by design, and if so, what are some examples of where this would be the preferred behaviour?


It is the preferred behavior for the following case.

def foo():
def printlocals():
print a,b,c,d
a = 1; b = 4; c = 5; d = 0.1
printlocals()
a = 2
printlocals()

When seeing a nested function, there are strong expectations by most peoplethat it will behave this way (not to mention it's a lot more useful). It's only for the less common and much more advanced case of creating a closure in a loop that the other behavior would be preferred.


Carl Banks
 

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

Forum statistics

Threads
473,731
Messages
2,569,432
Members
44,832
Latest member
GlennSmall

Latest Threads

Top