A closure is a technique for implementing lexically scoped name binding in a language with first-class functions.
Pairs
This post is triggered by this question and the nice answer by Martijn Pieters.
Here is a super short example:
def cons(a, b):
def pair(f):
return f(a, b)
return pair
def car(pair):
def return_first(x, y):
return x
return pair(return_first)
def cdr(pair):
def return_last(a, b):
return b
return pair(return_last)
cons_return = cons(42, 1337)
print(cons_return)
print(car(cons_return))
print(cdr(cons_return))
What is the output of the 3 print statements? Why?
As there are not too many possible answers, you can easily guess it. But can you explain it?
Explanation:
pair
is a function, hencecons
returns a function. Note that it returns the function, not the return value of the function. So the first output is something like<function cons.<locals>.pair at 0x7f63aa5fa840>
.- Once
pair
is called,a
andb
are set in this name space. Hencecons
can return different functions! car
andcdr
are basically the same, so let's just focus oncar
.car
has a function as its parameter. It calls this function and gives it a function that wants two parameters. This parameter function just returns its first argument.- Once
car
is called, the new functionpair
is called with the local context ofcons
. Thenreturn_first
is called within that context.
Once you print some intermediate results you get a better understanding:
def cons(a, b):
print("cons called with a={}, b={}".format(a, b))
def pair(f):
print("pair called with a={}, b={}, f={}".format(a, b, f))
return f(a, b)
return pair
def car(pair):
print("car called with pair={} (a, b are undefined)".format(pair))
def return_first(x, y):
print("return_first called with x={}, y={}".format(x, y))
return x
return pair(return_first)
def cdr(pair):
def return_last(a, b):
return b
return pair(return_last)
cons_return = cons(42, 1337)
print(cons_return)
print()
print(car(cons_return))
gives
cons called with a=42, b=1337
<function cons.<locals>.pair at 0x7fbb5fe7c840>
car called with pair=<function cons.<locals>.pair at 0x7fbb5fe7c840> (a, b are undefined)
pair called with a=42, b=1337, f=<function car.<locals>.return_first at 0x7fbb5fe7c8c8>
return_first called with x=42, y=1337
42
I don't know about you, but this really gives me headaches. I'm don't know why, but closures are super popular in JavaScript.
Let's see the one example I know they are super useful for.
partial
Pythons functools.partial
can be used to create simpler functions where some of the arguments are already
filled.
It's implementation is roughly:
def partial(func, *args, **keywords):
def newfunc(*fargs, **fkeywords):
newkeywords = keywords.copy()
newkeywords.update(fkeywords)
return func(*args, *fargs, **newkeywords)
newfunc.func = func
newfunc.args = args
newfunc.keywords = keywords
return newfunc
And you can use it like this:
from functools import partial
def power(base, exponent):
return base ** exponent
basetwo = partial(power, base=2)
print(basetwo(exponent=10)) # prints 1024
Please note that the call basetwo(10)
gives the error
Traceback (most recent call last):
File "/home/moose/foobar.py", line 8, in <module>
print(basetwo(10)) # prints 1024
TypeError: power() got multiple values for argument 'base'
as you supply power both as a keyword-argument and as a position-based argument.