10

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) 

3 Answers 3

11

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) 
Sign up to request clarification or add additional context in comments.

Comments

3

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.

Comments

0

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 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.