Update: You can find anewer version of this article on Python Morsels.
In this article, we’re going to look at a common programming pattern and discuss how we can refactor our code when we notice this pattern. 🏗
We’ll be discussing how to make code with this shape a little more descriptive:
12345 |
|
Here’s a function that checks whether a given number is prime by trying to divide it by all numbers below it:
12345 |
|
Note: asquare root makes this faster and our code breaks below2
but we’ll ignore those issues here
This function:
False
as soon as a divisor is foundTrue
if no divisor was foundThis primality check is asking “do any numbers evenly divide the candidate number”.
Note that this functionreturns as soon as it finds a divisor, so itonly iterates all the way through the number range when the candidate number is prime.
Let’s take a look at how we can rewrite this function usingall
.
all
?Python has a built-in functionall
that returnsTrue
if all items aretruthy
12345678 |
|
You can think oftruthy as meaning non-empty or non-zero. For our purposes, we’ll treat it as pretty much the same asTrue
.
Theall
built-in function is equivalent to this:
12345 |
|
Notice the similarity betweenall
and ouris_prime
function? Ouris_prime
function is similar, but they’re not quite the same structure.
Theall
function checks for the truthiness ofelement
, but we need something a little more than that: we need to check a condition on each element (whether it’s a divsior).
all
Our originalis_prime
function looks like this:
12345 |
|
If we want to useall
in this function, we need an iterable (like a list) to pass toall
.
If we wanted to be really silly, we could make such a list of boolean values like this:
12345678 |
|
We could simplify this function like this:
12345 |
|
I know this is probably doesn’t seem like progress, but bear with me for a few more steps…
If you’re familiar withlist comprehensions, this code structure might look a little familiar. We’re creating one iterable from another which is exactly what list comprehensions are good for.
Let’s copy-paste our way into a list comprehension (see my article onhow to write list comprehensions):
123456 |
|
That’s quite a bit shorter, but there’s a problem: we’rebuilding up an entire list just to loop over it once!
This is less efficient than our original approach, which only looped all the way whencandidate
was prime.
Let’s fix this inefficiency by turning our list comprehension into a generator expression.
Agenerator expression is like a list comprehension, but instead of making a list it makes agenerator object.
A generator is aniterator: generators don’t compute the items they contain until you loop over them. We’ll see what that means in a moment.
We can turn our list comprehension into a generator expression by changing the brackets to parentheses:
123456 |
|
Now our code doesn’t create a list to loop over. Instead it provides us with a generator that allows us to compute the divisibility of each number one-by-one.
We can make this code even more readable by putting that generator expression inside the function call (notice that we can drop the second set of parentheses):
12345 |
|
Note that because our generator is lazy, we stop computing divisibilities as soon as ourall
function finds a divisible number. So we end up calculatingcandidate % n != 0
only as many times as we did in our original function.
So we started with afor
loop, anif
statement, areturn
statement for stopping once we find a divisor, and areturn
statement for the case where our number had no divisors (when it’s prime).
12345 |
|
We turned all that into a generator expression passed to theall
function.
12345 |
|
I prefer this second approach (a generator expression withall
) because I find itmore descriptive.
We’re checking to see whether “all numbers in a range are not divisors of our candidate number”. That sounds quite a bit more like English to me than “loop over all numbers in a range and return False if a divisor is found otherwise return True”.
If you don’t find the behavior ofall
intuitive, you might find it easier to understand (and more English-like) when used withif
:
1234 |
|
You can always reformat your code to use anif
statement if you find it more readable.
any
orall
We’ve been working with theall
function, but I haven’t mentioned it’s counterpart: theany
function. Let’s take a look at howall
andany
compare.
These two expressions:
12345678 |
|
Are equivalent to these two expressions (because ofDeMorgan’s Laws):
12345678 |
|
So this code:
12345 |
|
Is feature-identical to this code:
12345 |
|
Both of them stop as soon as they find a divisor.
I find the use ofall
more readable here, but I wanted to mention thatany
would work just as well.
any
andall
All that explanation above was valuable, but how can we use this new knowledge to refactor our own code? Here’s a cheat sheet for you.
Anytime you see code like this:
12345 |
|
You can replace that code with this:
1234 |
|
Anytime you see code like this:
12345 |
|
You can replace it with this:
1234 |
|
Note thatbreak
is used in the code above because we’re not returning from a function. Usingreturn
(like we did inis_prime
) is another way to stop our loop early.
Python’sany
andall
functions weremade for use with generator expressions (discussionhere andhere). You can useany
andall
without generator expressions, but I don’t find a need for that as often.
Quick note:any(item == 'something' for item in iterable)
is the same as'something' in iterable
. Don’t useall
/any
for checking containment, usein
.
As you discover new Python idioms and new language features are invented, your code style will evolve. Your preferred code style may never stop evolving. Code style is not concrete: it’s a process.
I hope I’ve inspired you to embrace the use ofany
/all
with generator expressions for improved readability and code clarity.
If you’d like toget practice with theany
andall
functions right now, sign up for Python Morsels using the form below to get an exercise that benefits from using one of these two functions.
I made Python Morsels tohelp experienced programmers level up their Python skills every week.For more details on it, [see the Python Morsels][python morsels] website.
Hi! My name is Trey Hunner.
I help Python teamswrite better Python code throughPython team training.
I also help individualslevel-up their Python skills withweekly Python skill-building.
Python Team TrainingThe best way to improve your skills is towrite more code, but it's time consuming to figure out what code to write. I've madea Python skill-building service to help solve this problem.
Each week you'll get an exercise that'll help you dive deeper into Python and carefullyreflect on your own coding style. The first 3 exercises are free.
Sign up below forthree free exercises!
See thePython Morsels Privacy Policy.
This form is reCAPTCHA protected (see GooglePrivacy Policy &Terms of Service)
Need tofill-in gaps in yourPython skills? I send regular emails designed to do just that.
You're nearly signed up. You just need tocheck your email and click the link there toset your password.
Right after you've set your password you'll receive your first Python Morsels exercise.