Here are some resources for more information about topics covered in this lesson:
The Basics of Python Inner Functions
00:00 In the previous lesson, I gave an overview of the course. In this lesson, I will introduce you to inner functions. Let’s jump right into some code. The simplest version of an inner function is merely a function defined inside of another function. In the REPL, I’ll do just this.
00:38 and then, because I’ve out-dented, I’m back in the context of the outer function, printing a different message. Inside of the outer function, I can call the inner function, and that’s all I’m going to do in this function.
00:52 Let’s see it work. I called the outer function, and you see the message from the outer and then from the inner. To prove that the inner function is in a different scope, I’ll attempt to call it directly. That doesn’t work.
The function called
inside() has access to everything within the
mug() function’s scope, including its calling arguments. When I call
mug(), the f-string within the
inside() function can use the
stuff argument from
mug() and print it out. Let’s take a look at a more practical example.
02:01 In case you haven’t used the concept before, a recursive function is one that calls itself. This is a common pattern in computer science for doing computation where a result is based on a previous result in a sequence.
02:14 It is also a useful tool for working with data trees. For more information on recursion, see the articles linked in the details below. The code in the top area here is comprised of two functions.
02:39 Factorials are a perfect use of recursive functions, as each calculation is just the factorial of the number of 1 less than the number. Lines 9 through 13 check that the number being passed in meets the required conditions.
02:54 It has to be an integer and it has to be a positive number. Line 15 calls the function that actually does the work and is the one that will do recursion. Line 6 is where the magic happens. It calls itself.
03:23 The code I’ve used here, where there are two functions to do the recursion, is a common pattern. You often want to do a bit of work before starting the recursion and don’t want to do that pre-work on every call. In this case, the pre-work is just checking the parameters, but you still don’t want to do it every time.
This redone function,
second_factorial(), starts out the same as
first_factorial() and has the same purpose. What I’ve done here is moved the private
_recurse_factorial() inside of
second_factorial(), and the rest is the same.
04:34 I’m going to signal this as private by naming it specially,” to “You can’t actually see this from outside the module.” This is called encapsulation. Just to show that it’s the same, let me import it and run it as well.
And there is
24—a robust, happiness-causing number if there ever was. Choosing whether to use an inner function is a design decision. Let me show you three versions of the same code using different techniques so you can think about the differences. In the top area here, you see
process_hotspots(), a function that reads a CSV file containing data on Wi-Fi hotspots in New York City and prints some summary information. In this version, I’m using an inner function called
most_common_provider() that does the processing on the CSV file.
Counter objects are part of the
collections library and are a special instance of sets, where you can add the same thing into it and, instead of just keeping unique values, it also counts how many times a value was added.
Similar to the factorial example, some setup is being done here with the argument passed in, then the inner function is called. In this case, the container function’s
file argument is checked to see if it is a string or a file handle. If it is a string, it treats it like a filename, opens the corresponding file, and passes the handle into the inner function.
There are the results! 3,319 hotspots with LinkNYC being the most common provider. Now I’m going to do it again, this time using a file handle. I’ll use a context manager to open the file and get the handle, named
07:09 Here, I’ve refactored the code, removing the inner function and making it a private helper function in the module. Everything else is the same. When should you do this instead of using an inner function?
It’s a matter of preference and a matter of reuse. If there’s any chance at all that
_most_common_provider() could be used by any function besides
process_hotspots(), then it can’t be an inner function.
I’ve also changed how the filename versus file handle code is done. On line 6, I assume it is a handle, then check if it is a string. If so, I open it and overwrite
file_obj. The rest of the function now just uses that file handle. Honestly, of the three versions, this is my preferred one, but it doesn’t have an inner function in it, and that’s what this course is about. Again, the trade-off here is one of reusability. My gut is
_most_common_provider() won’t be reused in its current state, so it doesn’t need special treatment.
08:25 If you were to start adding features to this code, like counting boroughs or area codes in addition to providers, then you’d want to rethink what I’ve done. In all likelihood, you’d also want to separate the output portion from the statistic gathering portion, and you’d be reorganizing a bunch here.
08:40 Writing software is all about trade-offs and crystal balls. You try to see into the future. If you’re right, then there’s less work. If you’re wrong, you’ve either over-engineered or have some refactoring to do.
Become a Member to join the conversation.