Python Memory Management and Tips Transcripts
Chapter: Memory and functions
Lecture: Counting with closures

Login or purchase this course to watch this video and the rest of the course contents.
0:00 Well, that was pretty cool, but let me show you how far this idea of closures goes.
0:05 There's something we can do that probably will be surprising if you haven't seen it. So this is gonna be "counting_closure".
0:13 Start like this with our fmain as always. Normally, what happens if you want to have some function and some data state that's
0:21 tied to it, you would create a class, right? Classes have functions, they have fields and then they can create an instance
0:28 of them. You stick them together and they go around for the rest of their life doing that. But let me show you that closures for a limited scenario,
0:36 basically, where there's one function but multiple pieces of data, potentially, you can go around and do this same thing.
0:43 So I'm gonna write a function that creates another function, it creates a closure. So, I'm gonna say "def create_counter",
0:50 and this function is going to return another function. whose job is when you call it,
0:55 It's going to basically increment a counter by start from some position and have some step size and so on. So I'll start, which is an int, step,
1:02 which is an int, and it's going to return a callable over here. A thing you can call, basically a function.
1:11 Check this out. We're gonna go and say the current value is going to be start minus step. Why did we do this minus?
1:19 Because the first thing we're gonna do when we define our function inside, check this is out, it's not on the outer scope.
1:25 It's going to be the counter implementation. It takes no arguments, and yet it works with data.
1:32 So what it's gonna do is it's gonna say "current += step" and it's gonna return current. And because the first thing it does is increment it,
1:42 we want to go back one so we start at the start, and then as soon as we increment it, it goes step one back to where it was and then step, then
1:49 step, then step. Now, in order to tell it you want to use this variable as a closure capture thing, you have to say
1:55 "nonlocal current". Now the warnings go away and what we're gonna do, we're gonna return counter implementation without calling it.
2:05 Well, that looks kind of interesting, But watch this. I'll say, "create_counter" and let's say this one starts at 7 and it steps by 3.
2:15 Then we're gonna print out calling it c1 like this. It's a function, it's a callable, so we do like this. And let's call it a
2:21 couple of times. What's gonna happen? Well, the first time we've given it 7 and said to step by 3. So hopefully it's gonna give 7.
2:29 But then what about the rest of these? Is it going to remember where it was? What is it going to do? Let's just go and run this and find out.
2:38 Check that out. 7, 10, 13. We could create another one of these. We could create a c2, which is create a counter, it starts at zero and
2:47 goes by 10. And let's just part way through call c2 a couple times and I'll just say,
2:55 Make it a little obvious that these are the number 2's coming over. Check this out. So it goes 7, 10 and this one just carries on, 13, 16,
3:07 and this other one I made also kind of seems isolated. 0, 10, if we called it a couple more times, it would count up 10, 20, 30.
3:17 They have captured these variables and they hold onto them forever. As long as c1 is around,
3:24 it's intermediate variables, like current and whatnot, those things now have references pointing back to
3:30 them. They're not going to get cleaned up. Even though this function returned, and normally that would clean up this variable, It doesn't now. So,
3:38 the main take away from this section is that these functions can capture variables as a closure, and when they do,
3:46 those captured variables are no longer released until the function goes away.
3:51 And if, you know, sometimes these functions would turn out to be top level functions that never go away. So in some sense,
3:56 these could be like memory leaks. That may be okay, that may not be okay, but you need to be aware that when you capture these things through closure,
4:06 you're going to end up with possibly holding on to that data longer. As you think about what things were getting captured,
4:12 you might want to think what variables do I really need here? Are there times where I want to make sure they get cleaned up or something?
4:20 So this sort of takes you outside the bounds of normal cleanup for the variables and pushes it back to the life cycle, the actual function that was
4:28 created as part of the closure.


Talk Python's Mastodon Michael Kennedy's Mastodon