Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Closures in Python

I've been trying to learn Python, and while I'm enthusiastic about using closures in Python, I've been having trouble getting some code to work properly:

def memoize(fn):
    def get(key):
        return (False,)

    def vset(key, value):
        global get
        oldget = get
        def newget(ky):
            if key==ky: return (True, value)
            return oldget(ky)
        get = newget

    def mfun(*args):
        cache = get(args)
        if (cache[0]): return cache[1]

        val = apply(fn, args)
        vset(args, val)
        return val

    return mfun

def fib(x):
    if x<2: return x
    return fib(x-1)+fib(x-2)

def fibm(x):
    if x<2: return x
    return fibm(x-1)+fibm(x-2)

fibm = memoize(fibm)

Basically, what this is supposed to do is use closures to maintain the memoized state of the function. I realize there are probably many faster, easier to read, and in general more 'Pythonic' ways to implement this; however, my goal is to understand exactly how closures work in Python, and how they differ from Lisp, so I'm not interested in alternative solutions, just why my code doesn't work and what I can do (if anything) to fix it.

The problem I'm running into is when I try to use fibm - Python insists that get isn't defined:

Python 2.6.1 (r261:67515, Feb  1 2009, 11:39:55) 
[GCC 4.0.1 (Apple Inc. build 5488)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import memoize
>>> memoize.fibm(35)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "memoize.py", line 14, in mfun
    cache = get(args)
NameError: global name 'get' is not defined
>>> 

Seeing as I'm new to Python, I don't know if I've done something wrong, or if this is just a limitation of the language. I'm hoping it's the former. :-)

like image 853
Kyle Cronin Avatar asked Feb 03 '09 00:02

Kyle Cronin


People also ask

Why we use closures in Python?

Python closures help avoiding the usage of global values and provide some form of data hiding. They are used in Python decorators.

What is __ closure __ 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. This cell object also has an attribute called cell_contents, which returns returns the contents of the cell.

What are closures used for?

Closures are useful because they let you associate data (the lexical environment) with a function that operates on that data. This has obvious parallels to object-oriented programming, where objects allow you to associate data (the object's properties) with one or more methods.


1 Answers

The problem is in your scoping, not in your closures. If you're up for some heavy reading, then you can try http://www.python.org/dev/peps/pep-3104/.

If that's not the case, here's the simple explanation:

The problem is in the statement global get . global refers to the outermost scope, and since there isn't any global function get, it throws.

What you need, is an access specifier for variables in the enclosing scope, and not the global scope.

In python 3.0, as I've tested, the nonlocal keyword is exactly what you need, in the place of global.

nonlocal get
...

In python 2.x, I just removed the global get and the oldget references and it works properly.

like image 199
sykora Avatar answered Sep 24 '22 09:09

sykora