Applying Scopes With Closures
00:00 Oh good. You didn’t touch the dial after all. Or maybe I should attribute that to no one knowing what a dial is? Anyway, closures. Closures are a concept you’ll find in many programming languages.
00:12 Essentially, there are functions that can remember and reuse variables that were present at the time of their creation. In Python, there are special use cases of the enclosing scope.
00:21 When a nested function is created, it’s set to close over the variables of the enclosing scope, retaining that data, hence the term closure. This also allows the creation of stateful functions, a function that has a memory or state that can change between calls.
00:38 Since closures aren’t the main focus of this course, the following example will be more of an appetizer. If you’re interested in diving deeper into closures, I recommend checking out this code conversation, Exploring Scopes and Closures in Python.
00:53 In this example, you’ll write a function that returns a closure function that calculates a running mean of the numbers that have been passed into it across multiple calls.
01:02 Basically, it gets the average of all the numbers it’s ever seen. Because it’s a function that returns closure functions, you can also call it a closure factory.
01:11
Start by defining the outer function, mean
, def mean()
with no parameters. So here in what will be the enclosing scope, to calculate means you’ll need a running total as well as the length, the number of elements in the sequence.
01:25
Create two variables total
and length
, and set them both to zero. And start on the inner function, def _mean(number)
.
01:36
This will be the closure function returned by mean
. You add the prefix underscore to indicate that this inner function is internal to the factory function and not meant to be called directly.
01:47
So to allow the inner _mean
function to modify total
and length
, you first need to use the nonlocal
statement as you’ve seen before, nonlocal total, length
.
01:58
And now implement the logic for the running average. Increment total
by the value of number
as passed into the inner _mean
function.
02:09
and also increase the length of the sequence by one and writing the mean average, right? So return total
divided by length
.
02:19
Finally, back at the level of the outer function, return _mean
, the inner function itself.
02:25
Remember though mean
is a closure factory. So to get the running average function, you need to call mean()
and store the result in a variable with the name that you want that function to have because mean
is a function that returns functions.
02:41
and now your closure function is created. Try it out. Call current_mean(10)
. Well, the average of 10 is 10. Call current_mean(20)
.
02:53
Now the average of 10 and 20 is 15. Call current_mean(30)
. Seems to be working. The average of 10, 20, and 30 is indeed 20.
03:07
So how does this all work? Think about the variables in the current_mean()
function. Only number
is local to the function. The enclosed variables, length
and total
are considered free variables, used by the function but not bound to it.
03:23
And so they can persist in the closure despite the local scope being created and destroyed each time you call the function. Using the nonlocal
statement, the function can update their values repeatedly changing their state.
03:36 And honestly, all of this still kind of hurts my brain to think about, but that’s what we’re here for, isn’t it?
03:44 And that was closure functions. Speaking of sore brains, all that’s left to do is wrap things up in the summary.
Become a Member to join the conversation.