Reputation: 7360
I always thought that Python 2.7 functions refer to the scope they were defined in. Consider the following code. Why is the second output not "calculating: sin"?
Is there any way to modify the code so it is working as expected?
import math
mymath = dict()
for fun in ["sin", "cos"]:
def _impl(val):
print "calculating: %s" % fun
return getattr(math, fun)(val)
mymath[fun] = _impl
# calculating: cos
print mymath["cos"](math.pi)
# calculating: cos <- why?
print mymath["sin"](math.pi)
Upvotes: 10
Views: 845
Reputation: 421
I think you are trying to make the things harder: Here is how you can do it with closures:
import math
mymath = dict()
def funcmaker(fun):
print "creating %s function" % fun
def calculate(val):
print "calculating: %s" % fun
return getattr(math, fun)(val)
return calculate
print funcmaker("sin")(math.pi)
print funcmaker("cos")(math.pi)
Above code gives you the following result:
creating sin function
calculating: sin
1.22464679915e-16
creating cos function
calculating: cos
-1.0
Upvotes: 0
Reputation: 1070
From this code (which works as you intended)
my = {}
def makefun(fun):
def _impl(x):
print fun, x
return _impl
for fun in ["cos", "sin"]:
my[fun] = makefun(fun)
# will print 'cos'
my['cos'](1)
fun = 'tan'
# will print 'cos'
my['cos'](2)
it seems that it is not the namespace of the function definition which decides about the nature of the closure but instead the namespace of the used variables. More testing:
my = dict()
fun = ''
def makefun():
global fun #This line is switched on or off
fun = 'sin'
def _impl(x):
print fun, x
return _impl
test = makefun()
#gives sin 1
test(1)
fun = 'cos'
#gives sin 2 if line global fun is used
#gives cos 2 if line global fun is NOT used
test(2)
So the correct explanation seems to be that the closure saves a reference to its arguments and not a value.
Upvotes: 3
Reputation: 6326
The value of fun
is evaluated when the function is called.
In the example you provided, fun
is a global variable, and it's value is "cos" after the for
loop runs.
I think you expect the value of fun
to be substituted when you create the function, but it's not. The function evaluates the value of the variable when it runs just like it's supposed to.
It's not about the namespace in which you define the function, but the namespace in which you run the function.
import math
mymath = dict()
for fun in ["sin", "cos"]:
def _impl(val):
print "calculating: %s" % fun
return getattr(math, fun)(val)
mymath[fun] = _impl
fun = 'tan'
# will print and calculate tan
print mymath["cos"](math.pi)
Upvotes: 11