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
orFalse
.x
is converted using the standard truth testing procedure. Ifx
is false or omitted, this returnsFalse
; otherwise, it returnsTrue
.
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!