10

I'm wondering if it's possible for a closure in Python to manipulate variables in its namespace. You might call this side-effects because the state is being changed outside the closure itself. I'd like to do something like this

def closureMaker():
  x = 0
  def closure():
    x+=1
    print x
  return closure

a = closureMaker()
a()
1
a()
2

Obviously what I hope to do is more complicated, but this example illustrates what I'm talking about.

Mike
  • 58,961
  • 76
  • 175
  • 221
  • I'd mark this as a duplicate of other questions, except the other questions should really be marked a duplicate of this one; this question is quite simple and to-the-point and well-written. But also see http://stackoverflow.com/questions/141642/what-limitations-have-closures-in-python-compared-to-language-x-closures – ninjagecko Jul 04 '11 at 00:06
  • So there is no way to merge two or more questions? ;p – André Laszlo Jul 04 '11 at 00:26

2 Answers2

17

You can't do exactly that in Python 2.x, but you can use a trick to get the same effect: use a mutable object such as a list.

def closureMaker():
    x = [0]
    def closure():
        x[0] += 1
        print x[0]
    return closure

You can also make x an object with a named attribute, or a dictionary. This can be more readable than a list, especially if you have more than one such variable to modify.

In Python 3.x, you just need to add nonlocal x to your inner function. This causes assignments to x to go to the outer scope.

interjay
  • 107,303
  • 21
  • 270
  • 254
  • Please explain why? I'm trying to find something about this in the python docs. – André Laszlo Jul 03 '11 at 23:56
  • 1
    @André: PEP 3104 is informative, if technical: http://www.python.org/dev/peps/pep-3104/ – Thomas K Jul 04 '11 at 00:02
  • To me, it seems like it **should** work either way. I just read [this](http://docs.python.org/reference/executionmodel.html#naming-and-binding): *"If the definition occurs in a function block, the scope extends to any blocks contained within the defining one, unless a contained block introduces a different binding for the name."* – André Laszlo Jul 04 '11 at 00:02
  • Ah, so when you try to assign to `x` in the example given in the question - it doesn't work because you try to *rebind* the variable? Sneaky. – André Laszlo Jul 04 '11 at 00:12
  • 2
    @André: When a variable is assigned to in a function (this includes augmented assignment operators such as `+=`), then it is made a local variable of that function. So in the original code, `x` is a local variable of `closureMaker` (because of `x=0`), and *another* x is made a local variable of `closure` (because of `x+=1`). When using a list instead, there is no direct assignment to `x` in `closure`, so it uses the same variable as `closureMaker`. – interjay Jul 04 '11 at 00:15
  • Yeah, I finally got it. :) Thanks! – André Laszlo Jul 04 '11 at 00:18
5

What limitations have closures in Python compared to language X closures?

nonlocal keyword in Python 2.x

Example:

def closureMaker():
     x = 0
     def closure():
         nonlocal x
         x += 1
         print(x)
     return closure
Community
  • 1
  • 1
ninjagecko
  • 88,546
  • 24
  • 137
  • 145