Is There Any Doctor Here? We're All Doctors…
Python's built-in `any()` and `all()`, and a glimpse at short-circuiting
Only a few passengers were enjoying the never-ending sunset from their 40,000-foot vantage point. Many were asleep, some of them in awkward positions. Others were watching films on the postage stamp-sized screens. Five more hours of this.
But there was a bit of agitated movement around rows 20 to 22. Passengers started looking up from their screens to crane their necks and see what was going on.
They didn't have to wait long to find out. A few minutes later, an announcement on the plane's PA woke up most of those still asleep:
Is there any doctor on board?
I'm sure you've seen this scene play out in films or TV series. The rest of the action will surely be full of nail-biting drama. But you'll have to finish your own story of what happened on that plane. Here, we'll focus on how to help the airline staff find a doctor using Python code.
Any Doctor on Board?
Python Airlines includes a profession drop-down list on its booking form along with fields asking for all the usual information. Here's what a passenger entry looks like:
The dictionary contains keys and values for relevant attributes needed to store a passenger's details, including their profession. You can use a Passenger
class instead of a dictionary if you prefer.
Let's now see what the plane's manifest may look like:
You won't hear the "Is there any doctor on board?" announcement on Python Airlines. Instead, they use Python code to find out if there's any doctor.
The first solution
At first, they used the following code:
Hey Stephen, you've got a typo in the code. The
else
should have the same indentation as theif
, surely?!
If this thought popped into your head while reading the code above, then you're probably not familiar with the else
clause as part of the for
loop. I don't blame you—it's not very common. But yes, a for
loop can also have an else
clause. You'll see what this does soon, but this tutorial is not about the for..else
construct.
Let's go through the code above. The for
loop iterates through the passengers in the list of dictionaries. An if
statement checks whether the value associated with the "profession"
key for each passenger is "Doctor"
. And when the first doctor is encountered, the break
statement stops the for
loop since you only need to find one doctor. It doesn't matter if there are others.
When a for
loop breaks because of the break
statement, the program skips the code in the else
clause.
If there are no doctors on board, the for
loop will iterate through the entire list, and the code in the if
block never executes. Since the for
loop reaches the end without breaking, Python executes the code in the else
block.
Here's the output from this code:
A doctor is on board
You can confirm that the code stops when it encounters the first doctor by printing the name of the doctor that breaks the loop:
The output from this code shows the name of the doctor:
A doctor is on board, Dr Stephen Strange
The code only shows the name of the first doctor in the list of passengers, Dr Strange, ignoring Leonard McCoy, who's also a doctor. Strange comes before McCoy in the list of passengers.
Did you know you can now purchase my video courses individually? Pick the one you're interested in and start learning!
The second solution using any()
However, Python has a built-in function that's designed for this job. Let's first re-write the code using the built-in any()
function before diving into the details of what any()
does and how it works:
The output from this code shows there's a doctor on board:
A doctor is on board
Here's the if
statement again, and let's translate it into plain English:
## Python
if any(
passenger["profession"] == "Doctor"
for passenger in passengers
):
## English
# If _any_ passenger's profession is Doctor for each passenger in the list of passengers…
I'll leave it as an exercise for you to confirm that if there aren't any doctors in the list of passengers, the code calls the second print()
function, which states there are no doctors on board.
How Does any()
Work?
Python's built-in 'any()' accepts an iterable as its only argument. You can brush up on iterables in Iterable: Python's Stepping Stones.
The any()
built-in function looks through each element of the iterable and returns True
when it encounters the first truthy element. If none of the iterable's elements are truthy, any()
returns False
.
Here are some examples in a Python REPL:
In the first two examples, you pass a list of Booleans to any()
. If at least one is True
, then any()
returns True
. The third and fourth examples show that you can have any objects in the list, not just Booleans since every object is either truthy or falsy.
And there's nothing special about lists in this case. You can use any iterable:
You pass a tuple to any()
in the first example and a generator expression in the second.
It's OK to be lazy (sometimes)
Python evaluates its built-in any()
function lazily. Lazy evaluation is when Python stops an operation when it reaches the desired result without evaluating the rest of the steps unnecessarily. "What does that mean?" you may be thinking. Here's an example:
The iterable passed to any()
is a generator expression.
Can you spot what's weird about this code? After all, int("5")
returns the integer 5
, which is truthy. Therefore, any()
is meant to return True
, right? Right.
But the fourth element in the list is the string "Python"
, which cannot be converted into an integer using int()
. Try running int("Python")
as a standalone expression, and you'll get a ValueError
.
However, when evaluating the any()
function above, Python short-circuits the rest of the evaluation once it encounters one truthy value. In this case, the second item yielded by the generator is truthy. Therefore, Python doesn't evaluate the rest of the generator expression.
Here's a simplified set of steps to describe what's happening in this example:
The program calls the
any()
function with a generator expression as its argument.The generator expression evaluates
int("0")
, which returns0
.Since
0
is falsy, theany()
function requests the next item from the generator.The generator yields
5
next, which is truthy.Since
any()
found a truthy value, it doesn't need to keep looking further. It stops requesting items from the generator, and instead, it returnsTrue
. Its job is done.
Therefore, the final item in the list, the string "Python"
, is never passed to int()
, and the code never raises the ValueError
.
You can compare this with the version below, which replaces the string "5"
with "0"
in the second spot in the list:
Note that any()
doesn't return False
in this case. Instead, it raises an error. The generator yields 0
three times. Since all are falsy, any()
requests the next item from the generator. But this tries to evaluate int("Python")
, which is invalid and raises a ValueError
.
Reviewing the code looking for a doctor on the plane
Let's look at the code Python Airlines uses whenever they need to find out if there's a doctor on board:
The argument in any()
is a generator expression. The generator yields a Boolean each time the next value is needed since the expression in the generator expression compares the value associated with the "profession"
key with the string "Doctor"
using the equality operator ==
.
However, any()
stops requesting more values from the generator once it finds the first doctor. As long as there's one doctor on the plane, any()
is satisfied!
The Doctors' Conference
A few weeks later, Python Airlines was chartered to take a group of doctors to a conference. The airline staff wanted to ensure that all the passengers on the flight were doctors. Python's any()
isn't useful in this case. However, it's cousin all()
comes to the rescue:
The list of passengers is the same one you used earlier. However, the if
statement now includes a call to all()
rather than any()
. The built-in all()
returns True
if all its elements are truthy. In this case, there are some non-doctors on board:
Not all passengers are doctors
Let's update the plane's manifest:
The output confirms that all passengers are doctors, and what a collection of doctors are on board!
Great! All passengers are doctors
Short-circuiting in all()
Whereas any()
looks for at least one truthy value, all()
looks for at least one falsy one. As soon as there's a falsy value, all()
returns False
and short-circuits the rest of the evaluation since there's no reason to carry on. If one item is falsy, then all the values can't be truthy.
Here's an example similar to the one you used earlier:
The generator yields 5
, 10
, and 15
as the first three items. All are truthy, so all()
keeps requesting more items from the generator. However, the fourth item is the string "Python"
, and int("Python")
raises an error.
Now, replace one of the first three items with "0"
:
The generator yields 5
as its first item, which is truthy. However, it yields 0
next, which is falsy. Since all()
only returns True
if all the iterable's elements are truthy, it short-circuits the evaluation at this stage. It doesn't need to evaluate the rest of the generator. Therefore, the ValueError
is never raised.
Is There Any More to Say? No, That's All…
Once you're familiar with any()
and all()
, you'll start finding parts of your code you can simplify by using these built-in functions. These functions can make your code more readable and more Pythonic! And their short-circuiting behaviour can make your code more efficient, too!
Code in this article uses Python 3.12
Stop Stack
#69
Did you know you can now purchase my video courses individually? Pick the one you're interested in and start learning!
Thank you to all those who supported me with one-off donations. This means a lot and helps me focus on writing more articles and keeping more of these articles free for everyone. Here's the link again for anyone who wants to make a one-off donation to support The Python Coding Stack
The Python Coding Book is available (Ebook and paperback). This is the First Edition, which follows from the "Zeroth" Edition that has been available online for a while—Just ask Google for "python book"!
And if you read the book already, I'd appreciate a review on Amazon. These things matter so much for individual authors!
And for those who want to join The Python Coding Place to access all of my video courses—past and future—join regular live sessions, and interact with me and other learners on the members-only forum, here's the link:
Any questions? Just ask…
Appendix: Code Blocks
Code Block #1
a_passenger = {
"name": "Stephen Strange",
"dob": "1978-11-18",
"profession": "Doctor",
"nationality": "United States",
"passport_number": "M98765432",
}
Code Block #2
passengers = [
{
"name": "Stephen Strange",
"dob": "1978-11-18",
"profession": "Doctor",
"nationality": "United States",
"passport_number": "M98765432",
},
{
"name": "Hermione Granger",
"dob": "1979-09-19",
"profession": "Witch",
"nationality": "United Kingdom",
"passport_number": "G45678901",
},
{
"name": "James Bond",
"dob": "1920-11-11",
"profession": "Spy",
"nationality": "United Kingdom",
"passport_number": "G00000007",
},
{
"name": "Leonard McCoy",
"dob": "2227-01-20",
"profession": "Doctor",
"nationality": "United Earth",
"passport_number": "U12345678",
},
# Rest of passengers...
]
Code Block #3
# ...
for passenger in passengers:
if passenger["profession"] == "Doctor":
print("A doctor is on board")
break
else:
print("Oh no! No doctors on board")
Code Block #4
# ...
for passenger in passengers:
if passenger["profession"] == "Doctor":
print(f"A doctor is on board, Dr {passenger['name']}")
break
else:
print("Oh no! No doctors on board")
Code Block #5
# ...
if any(
passenger["profession"] == "Doctor"
for passenger in passengers
):
print("A doctor is on board")
else:
print("Oh no! No doctors on board")
Code Block #6
any([False, False, False])
# False
any([False, True, False])
# True
any([0, 0.0, "", {}])
# False
any([0, 0.0, "Python", {}])
# True
Code Block #7
any((0, 0, 1, 0, 1, 0))
# True
any(item - 5 for item in [5, 5, 10])
# True
Code Block #8
any(int(item) for item in ["0", "5", "0", "Python"])
# True
Code Block #9
any(int(item) for item in ["0", "0", "0", "Python"])
# Traceback (most recent call last):
# ...
# ValueError: invalid literal for int() with base 10: 'Python'
Code Block #10
# ...
if any(
passenger["profession"] == "Doctor"
for passenger in passengers
):
print("A doctor is on board")
else:
print("Oh no! No doctors on board")
Code Block #11
passengers = [
{
"name": "Stephen Strange",
"dob": "1978-11-18",
"profession": "Doctor",
"nationality": "United States",
"passport_number": "M98765432",
},
{
"name": "Hermione Granger",
"dob": "1979-09-19",
"profession": "Witch",
"nationality": "United Kingdom",
"passport_number": "G45678901",
},
{
"name": "James Bond",
"dob": "1920-11-11",
"profession": "Spy",
"nationality": "United Kingdom",
"passport_number": "G00000007",
},
{
"name": "Leonard McCoy",
"dob": "2227-01-20",
"profession": "Doctor",
"nationality": "United Earth",
"passport_number": "U12345678",
},
]
if all(
passenger["profession"] == "Doctor"
for passenger in passengers
):
print("Great! All passengers are doctors")
else:
print("Not all passengers are doctors")
Code Block #12
passengers = [
{
"name": "Stephen Strange",
"dob": "1978-11-18",
"profession": "Doctor",
"nationality": "United States",
"passport_number": "M98765432",
},
{
"name": "Leonard McCoy",
"dob": "2227-01-20",
"profession": "Doctor",
"nationality": "United Earth",
"passport_number": "U12345678",
},
{
"name": "Beverly Crusher",
"dob": "2324-10-13",
"profession": "Doctor",
"nationality": "United Earth",
"passport_number": "U87654321",
},
{
"name": "Gregory House",
"dob": "1959-06-11",
"profession": "Doctor",
"nationality": "United States",
"passport_number": "M12348765",
},
{
"name": "John Watson",
"dob": "1852-09-07",
"profession": "Doctor",
"nationality": "United Kingdom",
"passport_number": "K98765432",
},
]
if all(
passenger["profession"] == "Doctor"
for passenger in passengers
):
print("Great! All passengers are doctors")
else:
print("Not all passengers are doctors")
Code Block #13
all(int(item) for item in ["5", "10", "15", "Python"])
# Traceback (most recent call last):
# ...
# ValueError: invalid literal for int() with base 10: 'Python'
Code Block #14
all(int(item) for item in ["5", "0", "15", "Python"])
# False
Loved the plane analogy Stephen! I learned how to use the any built-in function perfectly! 🙌🏻
I liked the way you explained in the concept in a simple and easy to understand way, while also relating it practically with real life scenario. Keep writing more Stephen. 🙂