
Hang out with Python devs long enough, and you&aposll hear all about Tim Peter&aposs 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&aposs design. Recently I&aposve come to appreciate one aphorism more than the others: "Explicit is better than implicit."
The most common interpretation I&aposve seen — so common that it currently inhabits Google&aposs 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&aposs 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&apost see the function body and couldn&apost 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&apost 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&apost 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&aposs no way to eliminate it. But it isn&apost 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&aposs testability and re-usability. To see how, let&aposs refactor the find_big_numbers()
function from earlier.
How To Remove Implicit Input And Output
Here&aposs find_big_numbers()
again so you don&apost 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&aposs 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&aposs 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&apost 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&apost 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&apost 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.
Read more about implicit inputs and outputs in Eric Normand&aposs excellent book Grokking Simplicity. Get instant access from Manning* or order it on Amazon*.
*Affiliate link. See my affiliate disclosure for more information.
Want more like this?
One email, every Saturday, with one actionable tip.
Always less than 5 minutes of your time.