What Can A Coffee Machine Teach You About Python's Functions?
Let me tell you about one of my favourite analogies
I used to grind my own coffee beans. I had bought one of those old-fashioned-looking manual grinders with a metal funnel, a crank, and a tiny wooden drawer to collect the ground coffee.
I may even still have it somewhere.
I remember the muffled sound of the beans being crushed. I remember the aroma released directly into my nostrils. And I'd occasionally pop a whole coffee bean in my mouth as if it were a chocolate chip.
But life got busy. I now use a coffee machine. It's one of those that uses capsules to ensure maximum laziness—I mean, maximum efficiency. (I also use this joke when teaching programming to beginners when I tell them it's good to be lazy/efficient when coding.)
This article is about this coffee machine. Or any other coffee machine. It's not about coffee itself, so I won't be making any of those a-programmer-is-someone-who-converts-caffeine-into-code jokes.
But this article is also about Python functions. It's mostly about Python functions.
So, get yourself a cup of coffee—did I say no silly jokes? Sorry!—and join me for this journey that will, hopefully, brew some fresh insights in Python functions, one sip at a time. [Stop, Stephen. Stop!]
Those avid readers who've been reading my stuff for a long time may have read an article I wrote about the coffee machine analogy aeons ago, in the pre-The Python Coding Stack days. And if I taught you in a live course at some point, you probably already heard me describe this analogy!
The Coffee Machine. The Function
The coffee machine has a purpose—or, if you prefer, a function—and I use the word 'function' in its plain English meaning.
And the coffee machine has only one purpose—to make coffee.
It needs you to supply it with a few things before you can enjoy your coffee:
Water
Electricity
Coffee (let's say a coffee pod since that's the machine I have)
You supply these three things and press the ON button. But don't forget to put a cup or mug under the nozzle.
Stuff happens inside the machine. You don't really care what goes on. All you care about is the black gold that pours into your cup. Enjoy…
You have just called a function. The coffee machine is the function. I'm now using the programming meaning of the word 'function'.
You pass water, electricity, and coffee as arguments to the coffee machine function. You call the function when you press the ON button, and the coffee machine returns liquid coffee into your cup.
But let's dive further into this analogy. There's more to explore.
Did You Build Your Own Coffee Machine?
You buy a new coffee machine and unpack it from its box, but that's not the start of its existence.
Somewhere, someone built this coffee machine. They designed what's inside the machine and how its components interact with each other. Designing and building the coffee machine is equivalent to defining a function in Python:
However, it's best to name Python functions based on what they do rather than what they are. So let's rename this function:
It still represents the coffee machine. But now we're clearly showing that we care about the act of making coffee and not the machine itself—as it should be!
And, dear reader, unless you happen to be a coffee machine engineer, you probably don't know—and don't care—how the coffee machine works. All you care about is how to use the coffee machine. Add water, pop in a coffee pod, and plug in the machine to give it electricity. Press the button, and you'll get your coffee.
Defining a function and calling a function are different actions.
That's where the coffee machine's instruction manual comes in. For a function, this may be its docstring or the documentation page. The analogy is apt here, too. Many people don't read instruction manuals. And many people don't read the documentation either!
The coffee machine's instruction manual tells you a bit more about how you should use this function. You need to provide three things for the function (coffee machine) to work:
It also tells you that the coffee machine gives you liquid coffee once it finishes. I surely hope so!
So:
If you're a coffee machine engineer, you need to know what to put inside the function definition. You need to write
def make_coffee():
If you simply want a cup of coffee, you just need to know how to use the coffee machine. You need to call
make_coffee()
.
You call a function more often than you define one when you code. And you, like me, haven't got a clue what's inside most functions you call. Do you know what the code within def print():
looks like? Me neither…
You don't care about their definitions as long as you know what they do and how to use them.
But many times when coding, you're the engineer who builds the coffee machine. You need to define the function. Function definitions are a powerful tool every programmer relies on for efficient programming.
Making a Cup of Coffee
Right. You have a coffee machine, and you read the manual. You're ready to use it.
The function definition has three parameters: water
, electricity
, and coffee
. These parameters are the placeholders for the stuff you need.
So, water
is the label on the coffee machine's reservoir or tank. This is where you need to put the water.
The plug at the back of the machine is labelled electricity
. The slot where the coffee pod must go is labelled—you guessed it—coffee
.
However, when you use the coffee machine, you need to supply your own water, electricity, and coffee pods. You need to pass arguments to the function. These arguments are stored using the parameter names.
Let's call the make_coffee()
function:
Whereas the parameter names water
, electricity
, and coffee
are the labels used when defining the function, the arguments tap_water
, electricity_from_wall_socket
, and blue_espresso_intenso_pod
represent the actual things you use when you call the function—when you turn on the coffee machine.
You can pass other things to the function. For example, you may have one of those jugs that filter water, so you pass filtered_water
as the first argument. Or perhaps there are problems with your water supply, so you use bottled_water
. Maybe best to avoid sparkling_water
, though!
And what if you have an electricity-generating stationary bike that you can plug into the coffee machine? So, the second argument is electricity_from_stationary_bike
, and you also get a good workout each time you want a coffee.
You don't like espresso? Then use the caramel_pumpkin_spice_vanilla_latte_pod
instead (who drinks that stuff, anyway?)
You can pass any argument as long as it's an acceptable type. I used the word 'type' loosely in the previous sentence. Python is a duck-typing language, so what matters is not the type but some relevant characteristics.
For example, you could pass any non-poisonous liquid that doesn't damage the machine as the first argument. You could. Doesn't mean you should.
And the second argument is anything that has the ability to push electrons with the right oomph through the machine's electronics.
Turning the Machine On
The coffee machine has an ON button. You need to press it to get the machine to work. I think you didn't need an instruction manual for that.
What's the ON button of a Python function? The parentheses you add after the function's name perform this task.
Recall that the function make_coffee
represents the coffee machine. As I mentioned earlier, you could have called this function coffee_machine
in this example, but make_coffee
follows best naming practices.
There's a significant difference between writing make_coffee
and make_coffee()
in your Python code. The first one, make_coffee
, is the name of a function. It refers to the coffee machine itself. Let's look at the following Python line:
Let me refer to another analogy I use often. An assignment statement, when you assign an object to a variable name using the equals sign, stores a Python object in a box. In this example, the box is labelled output
. So, what goes inside this box?
The entire coffee machine!
Since make_coffee
refers to the coffee machine, you store the whole machine in the box output
. A function is an object in Python. Therefore, the function is stored in the variable output
.
Now, let's consider the following line:
OK, you're right! This won't work. But bear with me for a few short sentences. The parentheses are the function's ON button. You turn the coffee machine on, but you forget three important things. There's no water in the reservoir. This may damage the machine, but since there's no electricity either, no harm is done in this case.
You read the manual. You know you need to pass three arguments. Here's a second attempt:
The parentheses are there. Therefore, you turn the machine on, but this time, you also provide water, electricity, and the coffee pod.
The machine starts whizzing and purring and returns hot liquid coffee, which…
…is stored in a cup labelled output
. The return value is stored in the variable output
.
Don't Forget the Cup
Don't forget to assign the return value to a variable if you want to keep it. Consider this code:
You call the function. The parentheses are there. And all three arguments are correct.
The coffee machine starts to rumble. Half a second later, you can smell the coffee. Literally. The coffee starts streaming out…
…straight into the machine's overflow drain. You forgot to put the cup under the nozzle. I actually did this earlier this week—a clear sign I needed a coffee!
Unless you're prepared to tilt your head in an awkward position, open your mouth, and position it directly in the coffee stream's path—I don't recommend this—the coffee will go to waste.
That's why you need to assign the return value to a variable. Let's name the variable cup
this time:
And perhaps, you have another function called drink_beverage()
. It's reasonable to assume that this function needs an argument—the beverage. So, you may call drink_beverage(cup)
.
But you may also call the function as follows:
You call make_coffee()
directly within the parentheses of drink_beverage()
. When make_coffee()
returns a value, it goes straight into drink_beverage()
as its argument.
And yes, this represents the scenario where you stick your upturned head directly under the coffee machine's nozzle. Warning: you will burn your mouth!
Final Sips
I love the coffee machine analogy. It fits Stephen's Laws of Analogies perfectly. Here are these laws:
The analogy must describe an everyday scenario that everyone understands without too much mental effort.
There must be a great fit between the components of the analogy and the concepts it represents.
You've all seen a coffee machine—the first law is satisfied. And this article convinced you (I hope) that this analogy describes many of the basics of functions exceptionally well—put a tick next to the second law, too.
The usual analogy caveat applies: all analogies break down at some stage. So, you can't explain everything that goes on with Python functions using coffee machines. But drinking coffee may help with the rest!
Code in this article uses Python 3.12
Stephen!!!!
Wow, I am trying to understand the function concept from the last 5 days. You made me understand it in 10 mins.
Thankyou so much. Love you
I really love your analogies they work so well