Today we’ll be talking about closures, also known as factory functions.
A closure is a function that remembers values in enclosing scope even if that scope is no longer in memory. To understand how closures work, we definitely need an example.
If you prefer to watch a video first, here it is:
Here’s a function in which another function is nested. The outer function has the variable n in its local scope. It returns the inner function without calling it.
>>> def repeat(n):
... def set_word(word):
... return word * n # this n will be remembered!
... return set_word
...
>>> r7 = repeat(7) # r7 is a set_word function object
The variable r7 remembers the value of n from the enclosing repeat function even after being returned by it:
>>> r7("hi ")
'hi hi hi hi hi hi hi '
>>> r7("yes ")
'yes yes yes yes yes yes yes '
Now we can use the repeat function to return another set_word function object, this time with n = 4:
>>> r4 = repeat(4)
The value of n is still retained:
>>> r4("no ")
'no no no no '
And now let’s call the repeat function and pass the argument 2 to it:
>>> r2 = repeat(2)
>>> r2("good ")
'good good '
And now let’s have a look at a completely new example. Here the outer sliceN function returns the inner initial function, which remembers the value of n from the outer function:
>>> def sliceN(n):
... def initial(word):
... return word[:n]
... return initial
Let’s define a word that we’ll be using for demonstrative purposes:
>>> word = "unbelievable"
Now the sliceN function returns the initial function along with the value of n set to 3:
>>> s3 = sliceN(3)
The initial function still remembers the value:
>>> s3(word)
'unb'
And now let’s have some more examples with different values of n. Each time the value is retained even after leaving the outer function in the scope of which it was defined:
>>> s5 = sliceN(5)
>>> s5(word)
'unbel'
>>> s5("surprise")
'surpr'
>>> s1 = sliceN(1)
>>> s1(word)
'u'
>>> s1("hello")
'h'
It also works if the outer function returns a lambda expression instead of a function defined by the def statement. Have a look:
>>> def negate(word):
... return lambda prefix: prefix + word
...
>>> n1 = negate("happy")
>>> n1("un")
'unhappy'
>>> n2 = negate("legal")
>>> n2("il")
'illegal'