# Truthiness and Short-Circuit Evaluation in Python

Target audience: beginner programmers

In the high school Python class I’m helping out with, I’ve noticed that students will often write a chunk of code that looks like this:

In this example, the student has a `num` variable whose value is some integer, and they’re trying to write some code that gets run if the integer is `5` or `6` or `7`. The code snippet above seems reasonable at first glance, but it actually does something completely different from what the student would expect.

Let’s focus on the `num == 5 or 6 or 7` part, because that’s the part that isn’t doing what the student expects. Here’s what Python sees when you write that code:

num == 5
or
6
or
7

I’m going to be using a lot of diagrams like this throughout this article. In these diagrams, a yellow box is a chunk of code that Python hasn’t evaluated yet. (“Evaluated” basically means “run”.)

Notice how the first yellow box in that diagram is

num == 5

and the second box is

6

That second box isn’t `num == 6`—it’s just `6`. That’s kind of weird! What does the number `6` do if you put it in an `if` statement? Read on to find out!

OK, so we’re trying to decipher this code:

Let’s start our analysis by figuring out what that code does when the `num` variable has the value `10`.

Python starts by evaluating `10 == 5`, which turns into `False`.

False
or
6
or
7

So at this point, our partly-evaluated expression is `False or 6 or 7`, and Python has to figure out whether or not that whole thing ends up evaluating to `True`, because we’re running this code as the condition part of an `if` statement.

What does Python do when it sees `False or 6 or 7`? In order to answer that question, we’ll need to know about truthiness and short-circuiting.

# Truthiness

You’re familiar with the values `True` and `False`. We call them “Booleans”, and we use them most often in `if` statements.

``````
hungry = True

if hungry:
print('try eating a slice of pizza')
else:
print('must be nice')
``````

Python doesn’t limit us to just using `True` and `False` as the condition for `if` statements, though—you can put any expression in there. If you put something in an `if` statement’s condition section and it’s not `True` or `False`, Python will look at it and decide whether or not it’s “truthy”.

According to the official documentation, everything in Python is considered truthy except for these things:

• `False`
• `None`
• `0`
• Empty sequences, e.g. `[]`, `''`, `()`, `{}`

You can use the built-in `bool()` function to see if something is truthy. Here are some examples:

``````
print(bool(True))
print(bool(False))
print(bool('cat'))
print(bool([]))
print(bool(['pizza', 'tacos']))
``````

That code snippet is interactive, so go ahead and mess around with those examples to convince yourself that you understand how truthiness works. Is `15` truthy?

Now that we know what truthiness is, let’s talk about short-circuit evaluation.

# Short-Circuit Evaluation

The `and` and `or` operators in Python are short-circuit operators. To see what this means, let’s look at an example use of the `or` operator.

This is what Python sees before it starts evaluating that code:

1 == 1
or
1 == 2

Remember that if a box is yellow, that means that Python hasn’t evaluated it yet.

An `or` expression is truthy if at least one thing in it is truthy. An `and` expression is truthy if all things in it are truthy.

Since this is an `or`, Python evaluates each of the yellow boxes in order until it finds one that’s truthy. It starts by evaluating `1 == 1`, which turns into `True`.

True
or
1 == 2

At this point, Python stops, because you’re in an `or` and it’s found something truthy! That’s what short-circuiting means. The whole `or` expression evaluates to `True`, because that’s the value of the first truthy thing in it.

Here’s how the official documentation describes `or`’s behavior:

it only evaluates the second argument if the first one is false.

Here, I’ll prove it to you.

If you divide a non-zero number by zero, Python will throw an exception:

``````
1 / 0
``````

Now check out what happens if I put a `1 / 0` after a truthy thing in an `or`:

``````
print(True or 1 / 0)
``````

The program prints `True` and doesn’t evaluate the `1 / 0`! To convince yourself that this works the way I claim it does, try changing that `True` to a `False`.

This matches the behavior we saw in our most recent diagram. Do you remember how the `1 == 2` box stayed yellow to indicate that Python hadn’t evaluated the code inside of it?

So, that’s what “short-circuiting” means when you’re using the `or` operator. The `and` operator is pretty similar to `or`, except that the official documentation says that `and`

only evaluates the second argument if the first one is true.

That makes sense, because `and` wants to make sure that both of its operands are truthy. If the sub-expression on the left-hand side of an `and` is falsey, then the whole `and` expression is falsey! In that situation, there’s no need to evaluate the sub-expression on the right-hand side.

Here are some more examples. Do they all behave the way that you expect?

1 == 2
and
2 == 2
1 == 1
and
1 == 2
1 == 1
and
2 == 2
1 == 2
or
1 == 1

# Back to our buggy `num` code

Now that we know about truthiness and short-circuit evaluation, we can finally figure out what this code does!

What do you think will be printed out when that code is run?

Before we run it and find out for sure, let’s walk through one last set of diagrams using what we’ve learned. Here’s what Python sees before it starts evaluating anything:

num == 5
or
6
or
7

Python begins by evaluating `10 == 5`, which turns into `False`.

False
or
6
or
7

Next up, it evaluates `6`. We saw earlier that all non-zero numbers are truthy, so now our diagram looks like this:

False
or
6
or
7

At this point, Python stops and says: hey, I found something truthy! And that’s what the entire expression evaluates to. The answer is `6`!

``````
num = 10

print(num == 5 or 6 or 7)
``````

And so that’s why the code from the beginning of this article doesn’t do what our student expects. `num == 5 or 6 or 7` will always evaluate to either `True` or `6`, and so the code inside that `if` statement will always be run!

``````
num = 10

if num == 5 or 6 or 7:
1 / 0
else:
print('safe!')
``````

# Wrapping up

Here are a few more examples—play around with them and try adding some of your own!

``````
print(False or [])
print(2 or False)
print(False or 0 or "hello")
``````

Notice how if everything in an `or` is falsey, then the whole `or` expression will evaluate to the rightmost sub-expression.

``````
print(False or 0)
``````

If everything in an `and` is truthy, then the whole `and` expression will evaluate to the rightmost sub-expression.

``````
print('cat' and 'dog')
``````

Oh, and if you want to write some code that does what the student in our example actually wanted, try one of these:

``````
num = 7

print(num == 5 or num == 6 or num == 7)
print(num in [5, 6, 7])
print(5 <= num <= 7)
``````

By the way—what do you think this code does? Will it evaluate to `True`? If not, why not?

# Resources

If you've found an error or have a suggestion, please open an issue or pull request on GitHub.