Closures are an incredibly useful language feature. They let us do clever things that would otherwise take a lot of code, and often enable us to write code that is more elegant and more clear. In Python 2.x, closures variable names cannot be rebound; that is, a function defined inside another lexical scope cannot do something like some_var = 'changed!'
for variables outside of its local scope. Can someone explain why that is? There have been situations in which I would like to create a closure that rebinds variables in the outer scope, but it wasn't possible. I realize that in almost all cases (if not all of them), this behavior can be achieved with classes, but it is often not as clean or as elegant. Why can't I do it with a closure?
Here is an example of a rebinding closure:
def counter(): count = 0 def c(): count += 1 return count return c
This is the current behavior when you call it:
>>> c() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 4, in c UnboundLocalError: local variable 'count' referenced before assignment
What I'd like it to do instead is this:
>>> c() 1 >>> c() 2 >>> c() 3
The criteria that must be met to create closure in Python are summarized in the following points. We must have a nested function (function inside a function). The nested function must refer to a value defined in the enclosing function. The enclosing function must return the nested function.
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.
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.
To expand on Ignacio's answer:
def counter(): count = 0 def c(): nonlocal count count += 1 return count return c x = counter() print([x(),x(),x()])
gives [1,2,3] in Python 3; invocations of counter()
give independent counters. Other solutions - especially using itertools
/yield
are more idiomatic.
You could do this and it would work more or less the same way:
class counter(object): def __init__(self, count=0): self.count = count def __call__(self): self.count += 1 return self.count
Or, a bit of a hack:
def counter(): count = [0] def incr(n): n[0] += 1 return n[0] return lambda: incr(count)
I'd go with the first solution.
EDIT: That's what I get for not reading the big blog of text.
Anyway, the reason Python closures are rather limited is "because Guido felt like it." Python was designed in the early 90s, in the heyday of OO. Closures were rather low on the list of language features people wanted. As functional ideas like first class functions, closures, and other things make their way into mainstream popularity, languages like Python have had to tack them on, so their use may a bit awkward, because that's not what the language was designed for.
<rant on="Python scoping">
Also, Python (2.x) has rather odd (in my opinion) ideas about scoping that interferes with a sane implementation of closures, among other things. It always bothers me that this:
new = [x for x in old]
Leaves us with the name x
defined in the scope we used it in, as it is (in my opinion) a conceptually smaller scope. (Though Python gets points for consistency, as doing the same thing with a for
loop has the same behavior. The only way to avoid this is to use map
.)
Anyway, </rant>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With