The Function Room (Monty and The White Room Series #2)
Part 2 in the Monty and The White Room Series • Understanding functions
You're tired at the end of a long day, but you plan to write some code anyway. Monty is there, waiting for you as always. He's ready to go. He's not tired. He's not bored. He won't complain if you give him too much work. He won't make mistakes, either.
You met Monty in the first article in the Monty and The White Room mini-series. He's the avatar who reads and executes the instructions you write for him in your code.
Today, you'll define functions and use them in your code. How does Monty deal with functions? Let's find out.
Although you can read this article as a standalone post, it makes a lot more sense as a continuation of the first article in this series: Monty and The White Room.
You can also watch videos on The Python Coding Place’s YouTube channel covering this analogy.
Overview of the 'Monty and The White Room' series
Part 1 • Monty and The White Room. The first article introduces the analogy and its main character, Monty. It deals with the basic structure of a computer program, including importing modules, creating variables, and names. We also start to understand the concept of namespaces.
Part 2 (this article) • The Function Room. In Part 2, we extend the analogy to include defining and calling functions, including function arguments and return values. We also learn more about scope.
Part 3 • Python City. The series ends by zooming out from the "rooms" we're creating in our code base to look at how they're connected with other parts of the Python ecosystem.
I first introduced this analogy as an interlude chapter in The Python Coding Book: The White Room • Understanding Programming.
The Function Room
In the first article, you read about the White Room. This room starts empty at the beginning of a program. It has some shelves and just a single item on those shelves: a small red booklet called "built-ins". When you assign data to a variable name, Monty fetches an empty cardboard box and labels it using your chosen variable name. He puts the object he finds on the right-hand side of the equals in the box and places the box on one of the shelves. Later, if you use the name on the label anywhere in your code, Monty will bring the box down and get its contents.
And when you import a module, Monty leaves the white room and goes to the big library at the centre of Python City. He finds the book with the module name and takes it back to the White Room, where he places it on a shelf with the name facing outwards.
So, when you refer to any name in your program, Monty will look around the white room for a label with this name. He may find a box with an object inside it—a variable. Or he may find a book he had brought from the library earlier—a module. If he can't find the name anywhere, he'll look inside the red booklet, which was there from the beginning.
Defining a function • Building the Function Room
Let's write some code and define a function:
You run this code, and Monty springs into action. First, he reads import random
. He leaves the White Room, goes to the library, borrows the random
book, and takes it back to the White Room.
So, there is a red "built-in" booklet and a book named random
on the shelves so far.
In the following line, Monty reads def generate_number()
. Just as with import
, Monty knows what to do when he reads def
. He knows the Python keywords well.
A function is like a mini-program contained within another program. Therefore, Monty builds a new room for the function. This room is adjacent to the main white room. Once Monty finishes building the new room—he's quite quick—he puts a label on the door to the Function Room. The label says generate_number
, and it's on the side of the door that faces the main room.
Monty doesn't want to waste any time. So, he doesn't bother with the lines within the function definition for now. He doesn't need them yet.
Therefore, there are now two names that Monty will find in the White Room. There's a book labelled random
on one of the shelves and the closed door labelled generate_number
, which leads to another room. And then there are all the names in the red booklet that Monty can find if he needs them.
Calling The Function
"Nothing happens" when you run this code. But this is not quite true since Monty went to the library to fetch the random
book, built a new room adjacent to the main room, and labelled its door generate_number
. However, there's no output from the code.
Let's call the function:
When Monty reads the final line of code in this program, he encounters the name generate_number
. As with every name he reads, he looks around the room, searching for anything labelled with this name. He spots the name generate_number
on a label on a door. This tells him it's a function.
He also notices the parentheses ()
after the name generate_number
. This is Monty's cue to walk to the door labelled generate_number
, open it, and walk through it into the Function Room.
Monty is in the Function Room now. This is another empty room with shelves. Now, he starts reading the code in the function definition.
His first task is to fetch a cardboard box and label it number
. Monty places the random number he gets from random.randint()
in this box and places the box on one of the shelves. Remember that Monty is in the Function Room and not the main room.
He moves on to the second line of the function definition. There's a reference to the name number
on this line. Monty looks around him in the Function Room and finds the box with that label. It has a number inside it. Monty prints this number as instructed by the call to print()
.
He finished all the tasks required of him in the Function Room. So, he returns to the main White Room and closes the door to the Function Room behind him.
Calling The Function • Rewind and Play in Slow Motion
Let's rewind to when Monty read the function call generate_number()
. In the previous section, I went through the scene a bit too quickly and missed a few steps. Let's play the same scene in slow motion this time.
Monty reads generate_number()
, finds the label generate_number
on a door, opens the door, and walks through it. However, Monty leaves the door open while in the Function Room. This means he still has access to everything in the main room.
Let's see what happens as Monty is working on the first line in the function definition. Monty sees the equals sign and fetches an empty cardboard box. He labels it number
. Then he reads the name random
. He looks around the Function Room but doesn't find anything with this label. But since the door to the main room is open, he peeks into the main room and sees the book labelled random
there. All's good. He fetches the book from the main room and looks inside to find randint
.
We'll see what happens when Monty deals with random.randint()
in more detail in Part 3 of this series. For now, you can just assume that he gets a random number between 1 and 10 and places it in the box labelled number
in the Function Room.
A similar thing happens in the next line in the function definition. Monty looks for the name print
in the Function Room but doesn't find it. So, he looks in the main room since the door is still open. There, he finds print
on one of the pages in the red "built-in" booklet.
We'll also look at how Monty deals with print()
in the final article in the series.
Recall that once Monty finishes his tasks in the Function Room, he closes the door behind him as he returns to the main room. Therefore, when Monty is in the Function Room, the door is open, and he can access things that are either in the Function Room or the main White Room. But when Monty is in the main White Room, before or after the function call, the door to the Function Room is closed. So Monty doesn't have access to anything that's in the Function Room from the main room.
The names defined in the Function Room are local to the function. However, the names defined in the main White Room are global and can be accessed from any function room that's adjacent to the main room.
Monty Doesn't Return Empty-Handed
Let's add a return
statement to the function:
Monty fetches the random
book from the library and builds a Function Room when he reads the line with def generate_number()
.
When Monty reads the generate_number()
call, he opens the door labelled generate_number
, and enters the Function Room. He gets a random number between 1 and 10 and places it in a box labelled number
on the shelves in the Function Room. So far, this is identical to the previous example.
However, the final line of the function definition is different this time. There's a return
statement. This tells Monty it's time to return to the main room since his job is done here in the Function Room. But before he leaves, he'll need to take something back with him to the main White Room.
The keyword return
is followed by the name number
. Of course, Monty finds a box labelled number
in the Function Room.
And here's one of the most important nuances in this analogy. Ready?
Monty doesn't take the box labelled number
with him to the main White Room. He takes the contents of the box. So, I want you to picture Monty fetching the object stored in the box, which is a number between 1 and 10 in this example, and he takes this number with him back to the main room, leaving the box behind.
We'll see why this point is important soon.
Monty leaves the Function Room with a number in his hand. He closes the door of the Function Room behind him, and he's back in the main room.
But what should he do with the number he's holding? He can't keep holding it since he needs to free his hands for the next task he needs to do. Monty has no choice but to discard the number. He throws it away in the garbage.
Monty returns with the box's contents
Monty doesn't return to the main room with the box labelled number
but only with its contents. Let's confirm this:
We've already seen how Monty deals with importing the module, defining the function, and calling the function.
In the final line, Monty looks for something labelled number
. Remember that Monty is in the main White Room. There's nothing with this name in the main room. Monty can't carry on, and he tells you this:
Traceback (most recent call last):
...
print(number)
^^^^^^
NameError: name 'number' is not defined
There is a box labelled number
, but it's in the Function Room, behind the closed door labelled generate_number
. So, Monty doesn't have access to this box when he's working on the last line of code in the main program.
The last paragraph is technically incorrect since the contents of the Function Room disappear once Monty finishes his work there. Each time Monty leaves the Function Room, it "resets" for his next visit. But I won't dwell on this any longer.
Storing the returned object in the main room
Let's fix this code:
You added an assignment of data in the line that includes the function call. This means Monty knows what to do with the number he brings back from the Function Room. He stores it in a box labelled number
on the shelves of the main White Room.
This box has the same name as the box in the Function Room. But these are different boxes in different rooms. Let's use a different label for the box in the main room:
Parameters and Arguments
There's one more detail I'll add to the part of The White Room analogy that deals with functions. Let's add parameters to the function definition:
There are two parameters in the function definition: start
and stop
. When Monty builds the Function Room, he puts a small table next to the door. It's the first thing you see when you enter the Function Room. He puts two empty boxes on this table and labels them start
and stop
.
These boxes are already labelled, but they're empty. They're ready to be filled in once Monty enters the room during a function call. These are the function's parameters. They're pre-labelled empty boxes in the Function Room.
Let's call the function. This first attempt is incorrect, but let's look at it anyway:
When Monty reads the line with the function call, he opens the Function Room door and enters. However, he enters empty-handed. When he enters the room, he finds the two boxes labelled start
and stop
, but he doesn't have anything to put in the boxes. Empty boxes cause a problem for Monty, so he can't carry on:
Traceback (most recent call last):
...
some_number = generate_number()
^^^^^^^^^^^^^^^^^
TypeError: generate_number() missing 2 required positional arguments: 'start' and 'stop'
Let's call the function with arguments:
We'll pick Monty's adventure from the point when he reads the following line:
some_number = generate_number(1, 10)
He reads this line while he's in the main White Room. He gets a box and labels it some_number
, ready to put whatever comes back from the function in this box.
Next, he reads the name of the function, generate_number
, and the parentheses. However, there are some objects in the parentheses: two integers. Monty takes the integers 1 and 10 with him as he enters the Function Room.
Once he enters the Function Room, he places the first number in the first box labelled start
and the second number in the second box, stop
. He then places these boxes on the shelves in the Function Room. Whenever he encounters these names while in the Function Room, he'll find these boxes and fetch their contents.
And before you ask, no, I won't keep going with default arguments, named arguments, args and kwargs, and so on! If you've understood this far, you can carry on without the need for the Monty and The White Room analogy.
Monty Returns Empty-Handed
An earlier section in this article was called "Monty Doesn't Return Empty-Handed". What if he does?
Did you notice the difference from the previous version? The function definition no longer has a return
statement.
When Monty finishes his work in the Function Room, he returns to the main room empty-handed. But he needs to put something in the box labelled some_number
in the main White Room. He keeps an object in his pocket for these situations. It's a small, empty glass sphere. It represents nothingness or emptiness. Remember that Monty abhors empty boxes. So he puts this empty glass sphere in the box labelled some_number
to represent nothingness. This is the None
object in Python. Here's the output from the code shown above:
None
There's probably a long philosophical discussion we could have about the need for something to represent nothing.
But I won't!
Python City • Preview of Part 3
You've encountered two separate buildings in the Monty and The White Room analogy so far. There's the complex that contains the main White Room and any Function Rooms it has connected to it. And you saw the Central Library, which contains all the modules.
But there's more to see in Python City. What happens when Monty reads print()
in the red booklet or randint()
in the random
book?
We'll go sightseeing around Python City in the final article in this series.
To be continued in the final article in the series: Python City.
Code in this article uses Python 3.12
Stop Stack
#34
Recently published articles on The Python Coding Stack:
Monty and The White Room Understanding a Python program through The White Room analogy • Part 1
5:30am • Timezone Headaches (Part 1) I need Python's help to figure out the time of my talk • Dealing with timezones and daylight saving with Python's
zoneinfo
anddatetime
modules • The first article in a two-part mini-seriesA Slicing Story Are you sure you know everything there is to know about Python's
slice
object?Coding, Fast and Slow, Just Like Chess An essay: How adapting from fast to slow chess got me thinking about coding in Python
Butter Berries, An Elusive Delicacy (Paid article) How my quest to find butter berries at the supermarket led to musings about Python lists and dictionaries and more
Recently published articles on Breaking the Rules, my other substack about narrative technical writing:
The Consequential Detail (Ep. 12). Can a single letter or one blank line make a difference? (Spoiler Alert: Yes)
The Unexpected Audience (Ep. 11). What I'm learning from listening to Feynman's physics lectures
The Story So Far (Mid-Season* Review). Are you back from your holidays? Catch up with what you've missed
Broken Rules (Ep. 10). Let's not lose sight of why it's good to break the rules—sometimes
Frame It • Part 2 (Ep. 9). Why and when to use story-framing
The Rhythm of Your Words (Ep. 8). Can you control your audience's pace and rhythm when they read your article?
Stats on the Stack
Age: 6 months and 4 days old
Number of articles: 34
Subscribers: 1,134
Each article is the result of years of experience and many hours of work. Hope you enjoy each one and find them useful. If you're in a position to do so, you can support this Substack further with a paid subscription. In addition to supporting this work, you'll get access to the full archive of articles and some paid-only articles.