Step 3: Closures
The next thing to learn is the concept of a “closure.” A closure is any function that’s created inside another function but accesses data in its parent. Let’s look at this code to see a closure in action:
Listing 44.3: ex44_3.py
1 # this function makes functions 2 def constructor(color, size): 3 print(">>> constructor color:", color, "size:", size) 4 5 # watch the indent! 6 def repeater(): 7 # notice this function is using color, size 8 print("### repeater color:", color, "size:", size) 9 10 print("<<< exit constructor"); 11 return repeater 12 13 # what's returned are repeater functions 14 blue_xl = constructor("blue", "xl") 15 green_sm = constructor("green", "sm") 16 17 # see how these repeaters "know" the parameters? 18 for i in range(0,4): 19 blue_xl() 20 green_sm()
Breaking down this code, we have the following:
I start a function named def constructor(color, size), which will create functions for me.
I start off with a simple print() to trace this function.
Then I define the repeater() function, but notice it gets indented under constructor. This places that function inside constructor so that it’s only usable in that block.
Under def repeater(), I do a print(), but carefully look at what this print() line is using. It’s using the variables color and size from the def constructor(color, size), but those are function parameters. That means they’re temporary, and when constructor exits, they “die,” right? Nope.
Then I print another tracing line saying the constructor is exiting.
I return repeater() so the caller can have it, but remember color and size should be dead, right? Isn’t this an error?
After I’ve defined constructor, I use it to craft two repeater() functions named blue_xl and green_sm
I then have a for-loop that uses those two functions to repeatedly print the correct size and color I gave to constructor
This means that functions created inside other functions keep access to the variables they use.
The key here is how def repeater() is indented under the def constructor but tries to use color and size. Python detects this and creates a closure, which is a function that keeps references to any variables it used. These references are retained even when the parent function has long exited.
What You Should See
When you run this closure code, you should see the following output:
1 >>> constructor color: blue size: xl 2 <<< exit constructor 3 >>> constructor color: green size: sm 4 <<< exit constructor 5 ### repeater color: blue size: xl 6 ### repeater color: green size: sm 7 ### repeater color: blue size: xl 8 ### repeater color: green size: sm 9 ### repeater color: blue size: xl 10 ### repeater color: green size: sm 11 ### repeater color: blue size: xl 12 ### repeater color: green size: sm
If you get something different, review your constructor() to make sure you’ve indented things correctly.