views:

183

answers:

2
+8  Q: 

Closures in Python

I have seen & used nested functions in Python. They match the definition of a closure.

It is not closure simply because it is not used by external world?

UPDATE: I was reading about closures & it got me thinking about this concept with respect to Python. A little bit of search got me to the article someone in the comments pointed to. But I couldn't completely understand the explanation there. I thought about asking the experts here. So there...

+10  A: 

A closure occurs when a function has access to a local variable from an enclosing scope that has finished its execution.

def make_printer(msg):
    def printer():
        print msg
    return printer

printer = make_printer('Foo!')
printer()

When make_printer is called, a new frame is put on the stack with the compiled code for the printer function as a constant and the value of msg as a local. It then creates and returns the function. Because the function printer references the msg variable, it is kept alive after the make_printer function has returned.

So, if your nested functions don't

  1. access variables that are local to enclosing scopes,
  2. do so when they are executed outside of that scope,

then they are not closures.

Here's an example of a nested function which is not a closure.

def make_printer(msg):
    def printer(msg=msg):
        print msg
    return printer

printer = make_printer("Foo!")
printer()  #Output: Foo!

Here, we are binding the value to the default value of a parameter. This occurs when the function printer is created and so no reference to the value of msg external to printer needs to be maintained after make_printer returns. msg is just a normal local variable of the function printer in this context.

aaronasterling
You answer is much better than mine, you make a good point, but If we are going to go by the strictest functional programming definitions, are your examples even functions? It's been a while, and I can't remember if strict functional programming allows for functions that don't return values. The point is moot, if you consider the return value to be None, but that is a whole other topic.
mikerobi
@mikerobi, I'm not sure that we need to take functional programming into account since python isn't really a functional language although it certainly can be used as such. But, no, the inner functions are not functions in that sense since their whole point is to create side effects. It's easy to create a function that illustrates the points just as well though,
aaronasterling
@mikerobi: Whether or not a blob of code is a closure depends on whether or not it closes over its environment, not what you call it. It could be a routine, function, procedure, method, block, subroutine, whatever. In Ruby, methods can't be closures, only blocks can. In Java, methods can't be closures, but classes can. That doesn't make them any less of a closure. (Although the fact that they only close over *some* variables, and they cannot modify them, makes them next to useless.) You could argue that a method is just a procedure closed over `self`. (In JavaScript/Python that's almost true.)
Jörg W Mittag
@Jörg W Mittag, your a little late to the conversation. I had raised the point in my original answer that the reason for the OP confusion is mainly terminology. Aaron, criticized my answer for calling python functions closures, since you can create a function that isn't closed on any variables. He was right and I removed my answer. My comment about the definition of functions isn't really material to the discussion.
mikerobi
A: 

How about this code?

Below code they can access variables that are local to enclosing scopes, and they are executed outside of that scope.

def baby(input):
    name = [input];
    def set(input):
        name[0] = input
    return {"name": (lambda: name[0]), "set": set}

Excution example is below...

jason = baby("jason")
jason["name"]()
#output => 'jason'
jason["set"]("jason2")
jason["name"]()
#output => 'jason2'
bill = baby("bill")
bill["name"]()
#output => 'bill'
jason["name"]()
#output => 'jason2'

I think it is closure. Is it ok?

EveryEvery