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