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)
Closure in Python can be defined when a nested function references a value in its enclosing scope. Closures provide some form of data hiding. A closure can also be a highly efficient way to preserve state across a series of function calls.
Python closures A closure is a nested function which has access to a free variable from an enclosing function that has finished its execution. Three characteristics of a Python closure are: it is a nested function. it has access to a free variable in outer scope.
ADVANTAGE : Closures can avoid use of global variables and provides some form of data hiding. (Eg. When there are few methods in a class, use closures instead). Also, Decorators in Python make extensive use of closures.
__closure__ magic function in Python A closure is a function object that remembers values in enclosing scopes even if they are not present in memory. The __closure__ attribute of a closure function returns a tuple of cell objects.
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)
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With