This article contains affiliate links. See my affiliate disclosure for more information.
Hang out with Python devs long enough, and you'll hear all about Tim Peter's Zen Of Python.
The Zen, which you can conveniently read by executing import this
in a Python REPL, presents 19 of the 20 guiding principles behind Python's design. Recently I've come to appreciate one aphorism more than the others: "Explicit is better than implicit."
The most common interpretation I've seen — so common that it currently inhabits Google's featured snippet when searching for the phrase — is that verbose code is better than terse code because verbosity is, apparently, the key to readability... or something.
Sure, using better variable names and replacing magic numbers with named constants (or, in Python's case, "constants") are all great things. But when was the last time you hunted down implicit inputs in your code and made them explicit?
How To Recognize Implicit Inputs and Outputs
How many inputs and outputs does the following function have?
def find_big_numbers():
with open("numbers.txt", "r") as f:
for line in f:
if line.strip().isnumeric():
number = float(line)
if number > 100:
print(number)
find_big_numbers()
has no parameters and always returns None
. If you couldn't see the function body and couldn't access the standard output stream, would you even believe that this function does anything?
And yet, find_big_numbers()
has two inputs and another output besides None
:
numbers.txt
is an implicit input. The function won't work without it, but it is impossible to know that the file is required without reading the function body.- The magic number
100
on line 6 is an implicit input. You can't define a "big number" without it, but there is no way to know that threshold without reading the function body. - Values may or may not print to
stdout
, depending on the contents ofnumbers.txt
. This is an implicit output because the function does not return those values.
Implicit outputs are often called side effects.
Try It Yourself
Identify all of the inputs and outputs of the is_adult
function in this code snippet:
from datetime import date
birthdays = {
"miles": date(2000, 1, 14),
"antoine": date(1987, 3, 25),
"julia": date(2009, 11, 2),
}
children = set()
adults = set()
def is_adult(name):
birthdate = birthdays.get(name)
if birthdate:
today = date.today()
days_old = (today - birthdate).days
years_old = days_old // 365
if years_old >= 18:
print(f"{name} is an adult")
adults.add(name)
return True
else:
print(f"{name} is not an adult")
children.add(name)
return False
Why You Should Avoid Implicit Input And Output
One good reason is their penchant for violating the principle of least surprise.
Of course, not all implicit inputs and outputs are bad. A method like .write()
, which Python file objects use to write data to a file, has an implicit output: the file. There's no way to eliminate it. But it isn't surprising. Writing to a file is the whole point.
On the other hand, a function like is_adult()
from the previous code snippet does lots of surprising things. Less extreme examples abound.
Avoiding implicit input and output also improves your code's testability and re-usability. To see how, let's refactor the find_big_numbers()
function from earlier.
How To Remove Implicit Input And Output
Here's find_big_numbers()
again so you don't have to scroll up:
def find_big_numbers():
with open("numbers.txt", "r") as f:
for line in f:
if line.strip().isnumeric():
number = float(line)
if number > 100:
print(number)
Earlier, we identified two implicit inputs, the numbers.txt
file and the number 100
, and one implicit output, the values printed to stdout
. Let's work on the inputs first.
You can move the file name and the threshold value to parameters of the function:
def find_big_numbers(path, threshold=100):
with open(path, "r") as f:
for line in f:
if line.strip().isnumeric():
number = float(line)
if number > threshold:
print(number)
This has already dramatically improved the testability and re-usability. If you want to try it on a different file, pass the path as an argument. (As a bonus, the file can now be anywhere on your computer.) You can also change the threshold for "big numbers," if needed.
But the output is hard to test.
If you want to know that the function produced the correct values, you need to intercept stdout
. It's possible. But why not just return a list of all of the values:
def find_big_numbers(path, threshold=100):
big_numbers = []
with open(path, "r") as f:
for line in f:
if line.strip().isnumeric():
number = float(line)
if number > threshold:
big_numbers.append(number)
return big_numbers
Now find_big_numbers()
has an explicit return
statement that returns a list of big numbers found in the file.
find_big_numbers()
. How would you go about cleaning it up?You can test find_big_numbers()
by calling it with the path of a file whose contents are known and comparing the list returned to the list of correct values:
# test_nums.txt looks like:
# 29
# 375
# 84
>>> expected_nums = [375.0]
>>> actual_nums = find_big_numbers("test_nums.txt")
>>> assert(actual_nums == expected_nums)
find_big_numbers()
is more reusable now, too. You aren't limited to printing the numbers to stdout
. You can send those big numbers wherever you want.
Implicit inputs are data used by a function or program that aren't explicitly passed as arguments. You can eliminate implicit inputs by refactoring them into parameters.
Implicit outputs are data sent somewhere external to the function or program that aren't explicitly returned. You can remove explicit outputs by replacing them with suitable return values.
Not all implicit input and output can be avoided, such as functions whose purpose is to read or write data from files and databases or to send an email. Still, eliminating as many implicit inputs and outputs as possible improves the testability and re-usability of your code.
find_big_numbers()
?Curious about what happened to the 20th line in the Zen of Python? There are all sorts of theories floating around the internet. This one strikes me as reasonably likely.
What To Read Next
I used plenty of implicit input and outputs as a begging — even intermediate! — programmer. Here are four other mistakes I made:

Dig Deeper
Learn more about removing implicit inputs and outputs, as well as other strategies for dealing with complex code in Eric Normand's book Grokking Simplicity.
Get instant access from Manning, or buy a print version from Amazon.

Want more like this?
One email, every Saturday, with one actionable tip.
Always less than 5 minutes of your time.