Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a list of functions in python (python function closure bug?)

I understand functional programming well. I want to create a list of functions that each selects a different element of a list. I have reduced my problem to a simple example. Surely this is a Python bug:

fun_list = []
for i in range(5):
    def fun(e):
        return e[i]
    fun_list.append(fun)

mylist = range(10)
print([f(mylist) for f in fun_list])

"Obviously" it should return [0,1,2,3,4]. It however returns [4, 4, 4, 4, 4]. How can I coerce Python to do the right thing? (Hasn't this been noticed before? Or am I just being thick?)

This is Python 3.4.0 (default, Mar 25 2014, 11:07:05)

Thanks, David

like image 914
user3539674 Avatar asked Apr 16 '14 05:04

user3539674


People also ask

Are Python functions closures?

And all Python functions have a closure attribute so let's examine the enclosing variables associated with a closure function. The closure attribute returns a tuple of cell objects which contain details of the variables defined in the enclosing scope.

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.

What are closures in Python Mcq?

Closures in Python are the inner functions that have access to variables declared/initialized inside an outer function (enclosing function)even after the outer function has been executed completely or removed from the memory.

How does Python implement closures?

Closures use nested functions and free variables in their implementation. It is not necessary that the outer function should be alive when the nested function is executed i.e. the variables in the scope of the outer function may not be in the memory, still nested functions can have access to it.


2 Answers

How can I coerce Python to do the right thing?

Here is one approach:

fun_list = []
for i in range(5):
    def fun(e, _ndx=i):
        return e[_ndx]
    fun_list.append(fun)

mylist = range(10)
print([f(mylist) for f in fun_list])

This works because the default value for _ndx is evaluated and saved when the def statement for fun is executed. (In python, def statements are executed.)

like image 133
John1024 Avatar answered Sep 20 '22 19:09

John1024


Surely this is a Python bug...

This is a misunderstanding of scopes. Since all five instances of fun() are defined in the same scope, they will all have the same value of all names in that scope, including i. In order to fix this you need to separate the value used from the scope containing the loop itself. This can be done by defining the function within a completely different scope.

fun_list = []

def retfun(i):
  def fun(e):
    return e[i]
  return fun

for i in range(5):
  fun_list.append(retfun(i))

mylist = range(10)
print([f(mylist) for f in fun_list])
like image 26
Ignacio Vazquez-Abrams Avatar answered Sep 19 '22 19:09

Ignacio Vazquez-Abrams