The Built-In bool() Class

by Christoph Schiessl on Python

All objects in Python — without exception — can be converted to boolean values, which are either true or false. Booleans, in Python, are represented by objects of the bool class, which has exactly two constant instances: True and False. The bool class is, of course, callable like all classes are. The class, class bool(x=False), is officially documented as follows:

Return a Boolean value, i.e. one of True or False. x is converted using the standard truth testing procedure. If x is false or omitted, this returns False; otherwise, it returns True.

To explore this, we can start with a few trivial cases:

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.
>>> bool()      # default parameter value is False
False
>>> bool(False) # the constant False is obviously False,
False
>>> bool(True)  # and the constant True is obviously True
True

Nothing surprising here. We can go one step further and demonstrate that there are only two instances of the bool class:

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.
>>> type(True), type(False)
(<class 'bool'>, <class 'bool'>)
>>> id(False), id(bool()), id(bool(False))
(138814996967520, 138814996967520, 138814996967520)
>>> id(True), id(bool(True))
(138814996967488, 138814996967488)

It doesn't matter how you get True or False; there are only two constant instances, as you can tell from the object identities.

Standard Truth Testing Procedure

This procedure converts any object to True or False. By default, objects are considered to be True, unless certain conditions are met. This whole procedure is built on top of two magic methods that classes can, but don't have to, implement: __bool__() and __len__().

First off, objects are assumed to be True if they don't implement any of these magic methods:

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.
>>> dir(object())               # list of all methods/attributes of object()
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
>>> '__bool__' in dir(object()) # class object does not implement __bool__()
False
>>> '__len__' in dir(object())  # class object does not implement __len__()
False
>>> bool(object())              # therefore, object() is, by default, assumed to True
True

Next up, we can demonstrate that __bool__() is actually called when we trigger the truth testing procedure by calling bool():

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(object):
...     def __bool__(self):
...         print("IAmTrue.__bool__() has been called.")
...         return True
...
>>> bool(IAmTrue())
IAmTrue.__bool__() has been called.
True
>>> class IAmFalse(object):
...     def __bool__(self):
...         print("IAmFalse.__bool__() has been called.")
...         return False
...
>>> bool(IAmFalse())
IAmFalse.__bool__() has been called.
False

This works as expected and is precisely the same for built-in classes. For instance, NoneType (i.e., the class of None), implements __bool__() to always return False. Therefore, the statement bool(None) == False is always correct. Another prominent example is the int class, which implements __bool__() to return False for the number 0 and True for all other numbers.

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.
>>> '__bool__' in dir(int)      # the class int does implement __bool__(),
True
>>> '__len__' in dir(int)       # but it doesn't implement __len__()
False
>>> bool(0), bool(1), bool(2)   # all numbers other than 0 convert to True
(False, True, True)
>>> bool(-1), bool(-2)          # even negative numbers convert to True
(True, True)

The second magic method that the truth testing procedure depends on is __len__(). We can demonstrate this again:

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(object):
...     def __len__(self):
...         print("IAmTrue.__len__() has been called.")
...         return 1
...
>>> bool(IAmTrue())
IAmTrue.__len__() has been called.
True
>>> class IAmFalse(object):
...     def __len__(self):
...         print("IAmFalse.__len__() has been called.")
...         return 0
...
>>> bool(IAmFalse())
IAmFalse.__len__() has been called.
False

The logic here is that everything with a length other than 0 is converted to True. For instance, we can see this behavior in the built-in list class. Namely, bool([]) == False, because the length of the empty list is equal to 0. However, non-empty lists are converted to True, because their length is greater than 0 (e.g., bool([1,2,3]) == True).

Lastly, I want to mention that __bool__() takes precedence over __len__() for classes that have implemented both. Observe:

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(object):
...     def __bool__(self):
...         print("IAmTrue.__bool__() has been called.")
...         return True
...     def __len__(self):
...         print("IAmTrue.__len__() has been called.")
...         return 0
...
>>> bool(IAmTrue())
IAmTrue.__bool__() has been called.
True
>>> class IAmFalse(object):
...     def __bool__(self):
...         print("IAmFalse.__bool__() has been called.")
...         return False
...     def __len__(self):
...         print("IAmFalse.__len__() has been called.")
...         return 1
...
>>> bool(IAmFalse())
IAmFalse.__bool__() has been called.
False

This procedure is essential to understand because the bool class isn't the only way to trigger it. The same procedure is implicitly used, for example, to evaluate the conditions of an if statement:

>>> print("Correct" if IAmTrue() else "Incorrect")
IAmTrue.__bool__() has been called.
Correct
>>> print("Correct" if IAmFalse() else "Incorrect")
IAmFalse.__bool__() has been called.
Incorrect

This is everything for today. Thank you for reading, and see you soon!

Ready to Learn More Web Development?

Join my Mailing List to receive one article per week.


I send one email per week on building performant and resilient Web Applications with Python, JavaScript and PostgreSQL. No spam. Unscubscribe at any time.

Continue Reading?

Here are a few more Articles for you ...


Boolean Operators and Short-Circuiting

Fully understand Python's boolean operators for negation, conjunction, and disjunction. Master operator precedence and short-circuiting.

By Christoph Schiessl on Python

The Built-In any() Function

Learn how to use the built-in any() function in Python to determine if any element in an iterable is True, with implementation and performance insights.

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

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 the more than a decade of experience I have collected 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 also an avid writer and educator who takes pride in breaking down technical concepts into the simplest possible terms.