Boolean Operators and Short-Circuiting

by Christoph Schiessl on Python

Recently, I have written about various topics related to Booleans in Python, such as the built-in any() / all() functions and the Standard Truth Testing Procedure. But, what I did not yet explain about are three boolean operators themselves:

  • Negation with the not keyword,
  • Conjunction with the and keyword, and
  • Disjunction with the or keyword.

Negation with the Unary not Operator

The not operator is the simplest of all the boolean operators. Firstly, it's a unary operator, which takes only a single operand as input. Secondly, the operator's evaluation result is always True or False (i.e., an instance of the bool class). Thirdly, there is no short-circuiting behavior — the whole concept isn't even applicable to unary operators.

Python 3.12.2 (main, Feb 17 2024, 11:13:07) [GCC 13.2.1 20230801] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> class IAmTrue:
...     def __init__(self, label): self._label = label
...     def __repr__(self): return f"IAmTrue(label='{self._label}')"
...     def __bool__(self):
...         print("IAmTrue.__bool__() has been called.")
...         return True
...
>>> class IAmFalse:
...     def __init__(self, label): self._label = label
...     def __repr__(self): return f"IAmFalse(label='{self._label}')"
...     def __bool__(self):
...         print("IAmFalse.__bool__() has been called.")
...         return False
...
>>> not IAmTrue('1st')
IAmTrue.__bool__() has been called.
False
>>> not IAmFalse('1st')
IAmFalse.__bool__() has been called.
True

Conjunction with the Binary and Operator

Next up is the and operator, which is more interesting because it's a binary operator, meaning it takes two operands as input. The result is that expressions like x and y evaluate to x if and only if x converts to False; otherwise, they evaluate to y. This behavior is known as short-circuiting because — as long as the first operand converts to False — it doesn't even consider the second operand. Furthermore, note that, in all cases, only the first operand is ever truth-tested!

Python 3.12.2 (main, Feb 17 2024, 11:13:07) [GCC 13.2.1 20230801] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> class IAmTrue:
...     def __init__(self, label): self._label = label
...     def __repr__(self): return f"IAmTrue(label='{self._label}')"
...     def __bool__(self):
...         print("IAmTrue.__bool__() has been called.")
...         return True
...
>>> class IAmFalse:
...     def __init__(self, label): self._label = label
...     def __repr__(self): return f"IAmFalse(label='{self._label}')"
...     def __bool__(self):
...         print("IAmFalse.__bool__() has been called.")
...         return False
...
>>> IAmFalse('1st') and IAmFalse('2nd') # short-circuit to 1st
IAmFalse.__bool__() has been called.
IAmFalse(label='1st')
>>> IAmFalse('1st') and IAmTrue('2nd')  # short-circuit to 1st
IAmFalse.__bool__() has been called.
IAmFalse(label='1st')
>>> IAmTrue('1st') and IAmFalse('2nd')
IAmTrue.__bool__() has been called.
IAmFalse(label='2nd')
>>> IAmTrue('1st') and IAmTrue('2nd')
IAmTrue.__bool__() has been called.
IAmTrue(label='2nd')

Disjunction with the Binary or Operator

Last but not least is the or Operator, also a binary operator. The result is that expressions like x or y evaluate to x if and only if x converts to True; otherwise, they evaluate to y. This is again short-circuiting behavior: As long as the first operand converts to True, it doesn't even consider the second operand. Furthermore, note that, in all cases, only the first operand is ever truth-tested!

Python 3.12.2 (main, Feb 17 2024, 11:13:07) [GCC 13.2.1 20230801] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> class IAmTrue:
...     def __init__(self, label): self._label = label
...     def __repr__(self): return f"IAmTrue(label='{self._label}')"
...     def __bool__(self):
...         print("IAmTrue.__bool__() has been called.")
...         return True
...
>>> class IAmFalse:
...     def __init__(self, label): self._label = label
...     def __repr__(self): return f"IAmFalse(label='{self._label}')"
...     def __bool__(self):
...         print("IAmFalse.__bool__() has been called.")
...         return False
...
>>> IAmFalse('1st') or IAmFalse('2nd')
IAmFalse.__bool__() has been called.
IAmFalse(label='2nd')
>>> IAmFalse('1st') or IAmTrue('2nd')
IAmFalse.__bool__() has been called.
IAmTrue(label='2nd')
>>> IAmTrue('1st') or IAmFalse('2nd')  # short-circuit to 1st
IAmTrue.__bool__() has been called.
IAmTrue(label='1st')
>>> IAmTrue('1st') or IAmTrue('2nd')   # short-circuit to 1st
IAmTrue.__bool__() has been called.
IAmTrue(label='1st')

Operator Precedence

Finally, we have to talk about precedence. Here are the three boolean operators ordered from highest to lowest precedence: (1) not, (2) and, (3) or. As always, I want to demonstrate what I'm saying ... so, to prove that not has higher precedence than and/or, we need two boolean variables:

for x in [True, False]:
    for y in [True, False]:
        # Proof: `not` has higher precedence than `and`
        assert (not x and y) == ((not x) and y)
        assert (x and not y) == (x and (not y))
        # Proof: `not` has higher precedence than `or`
        assert (not x or y) == ((not x) or y)
        assert (x or not y) == (x or (not y))

And three variables to prove that and has higher precedence than or:

for x in [True, False]:
    for y in [True, False]:
        for z in [True, False]:
            # Proof: `and` has higher precedence than `or`
            assert (x and y or z) == ((x and y) or z)
            assert (x or y and z) == (x or (y and z))

If you run these two scripts, you don't get any AssertionErrors, proving that precedence works, as I said before. Anyway, that is everything for today! Thank you for reading, and see you soon!

Christoph Schiessl

Hi, I'm Christoph Schiessl.

I help you build robust and fast Web Applications.


I'm available for hire as a freelance web developer, so you can take advantage of my more than a decade of experience working on many projects across several industries. Most of my clients are building web-based SaaS applications in a B2B context and depend on my expertise in various capacities.

More often than not, my involvement includes hands-on development work using technologies like Python, JavaScript, and PostgreSQL. Furthermore, if you already have an established team, I can support you as a technical product manager with a passion for simplifying complex processes. Lastly, I'm an avid writer and educator who takes pride in breaking technical concepts down into the simplest possible terms.

Continue Reading?

Here are a few more Articles for you ...


The Built-In bool() Class

Learn about boolean values in Python and the standard truth testing procedure. Understand how objects are converted to True or False.

By Christoph Schiessl on Python

The Built-In callable() Function

Learn about the callable() function in Python. This article explains how everything in Python is potentially callable, including classes and instances.

By Christoph Schiessl on Python

Encapsulation: Public, Protected, and Private Members

Learn about encapsulation in Python. This article explains public, protected, and private members and the conventions used to implement them.

By Christoph Schiessl on Python

Web App Reverse Checklist

Ready to Build Your Next Web App?

Get my Web App Reverse Checklist first ...


Software Engineering is often driven by fashion, but swimming with the current is rarely the best choice. In addition to knowing what to do, it's equally important to know what not to do. And this is precisely what my free Web App Reverse Checklist will help you with.

Subscribe below to get your free copy of my Reverse Checklist delivered to your inbox. Afterward, you can expect one weekly email on building resilient Web Applications using Python, JavaScript, and PostgreSQL.

By the way, it goes without saying that I'm not sharing your email address with anyone, and you're free to unsubscribe at any time. No spam. No commitments. No questions asked.