Curious case of closure in Go and Python

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

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:

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.

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:

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:

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:

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.

One thought on “Curious case of closure in Go and Python

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s