Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding Python Closures

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)
like image 223
muffel Avatar asked May 18 '15 08:05

muffel


People also ask

How do closures work in Python?

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.

What are closers in Python?

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.

Why we use closures in Python?

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.

What is __ closure __ Python?

__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.


2 Answers

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)
like image 111
Ionut Hulub Avatar answered Oct 25 '22 03:10

Ionut Hulub


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.

like image 20
Nils_M Avatar answered Oct 25 '22 03:10

Nils_M