Curious case of closure in Go and Python

At http://tour.golang.org/#39, I found the following sample code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import "fmt"

func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}

func main() {
pos, neg := adder(), adder()
for i := 0; i < 10; i++ {
fmt.Println(
pos(i),
neg(-2*i),
)
}
}

Its document reads:

… functions are full closures. The adder function returns a closure. Each closure is bound to its own sum variable.

Take that into consideration, it might be easier to understand the execution result:

1
2
3
4
5
6
7
8
9
10
0 0
1 -2
3 -6
6 -12
10 -20
15 -30
21 -42
28 -56
36 -72
45 -90

To me, variable sum is similiar to ‘instance variable’, in Object-oriented’s terminology. However, in Python, things can be quite different.

1
2
3
4
5
6
7
8
9
10
11
12
def adder():
sum = 0

def f(x):
sum += x
return sum
return f

def main():
pos, neg = adder(), adder()
for i in xrange(0, 10):
print pos(i), neg(-2 * i)

The code looks roughly the same, but it will raise the following exception:

1
2
3
4
5
6
7
8
Traceback (most recent call last):
File "closure.py", line 17, in
main()
File "closure.py", line 13, in main
print pos(i), neg(-2 * i)
File "closure.py", line 5, in f
sum += x
UnboundLocalError: local variable 'sum' referenced before assignment

This is because if sum is to be modified, Python must decide which variable to change. **sum += x** is the same as **sum = sum + x**, and **sum = ** suggests it’s a local variable, since all variable is by default local in Python. Given that, expression **sum + x** can not be evaluated because sum, as a local variable, is still undefined here.

If the **sum += x** line is removed, and **sum + x** is returned directly, the result will be:

1
2
3
4
5
6
7
8
9
10
0 0
1 -2
2 -4
3 -6
4 -8
5 -10
6 -12
7 -14
8 -16
9 -18

It runs okay, but the result is wrong. Where does function f get the value of sum? If Python cannot find a variable in locals(), it will try to find it from the scope above it, i.e. function adder, and sum is indeed defined in it. The real Python equivelent of the Go program above will be:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class adder:
def __init__(self):
self.sum = 0
def __call__(self, x):
self.sum += x
return self.sum

def main():
pos, neg = adder(), adder()
for i in xrange(0, 10):
print pos(i), neg(-2 * i)

if __name__ == '__main__':
main()

Functions are already first class objects in Python. Here we create a class that its instance behaves like a function, so it is a function because of duck typing.

P.S. A reader is so kind to point out that I can use the `nonlocal’ keyword in python3. http://www.python.org/dev/peps/pep-3104/