The Python bool() built-in function evaluates the Truthiness of an object. Values resolved to 0 are evaluated as False whereas anything else is evaluated as True. The common case of Python’s bool() function is the conversion of values such that they can be evaluated in conditional statements.
The Python bool()
method operates mostly as one might expect—False
is False
, empty collections are False
, and 0
, 0.0
, and 0J
are all False
. There are some caveats and gotchas that can arise depending on how clever one tries to be. In this article, you’ll learn the basic syntax of the bool()
function, common cases for its use, advanced usages with custom classes and objects, and a few gotchas to look out for.
TL;DR – bool()
converts any object into a True
or False
value
# Returns True for any object that evaluates # to a non-zero value. >>> bool(0), bool(0j), bool(0.0), bool(1), bool(42) False, False, False, True, True >>> bool([]), bool({}), bool(()), bool('') False, False, False, False
Highlights
- bool() evaluates an objects for truthiness
- Any non-zero value is regarded as True
- bool() works with lists, dicts, tuples, generators, ints, strings, floats, and even complex numbers
- bool() uses the __len__ value for collection objects or __bool__ for non-collection items.
- bool() will fallback to the __len__ attribute for custom objects where __bool__ is not implemented.
Syntax & Common Cases
Python’s bool()
function is used to convert values into True
or False
. The return values of bool()
can then be used, largely for convenience, within conditional statements to direct the control flow of your program. Consider the following examples of the bool()
function demonstrating its evaluation of various types of input, values, and objects.
Numbers (ints, floats, and complex)
Any computer programming language I have experience with uses 0 and 1 to represent False and True, respectively. This is reflected in Python’s as evidenced by the bool()
function’s evaluation.
# Bool evaluates to True for 0 # and False for 1. Standard-issue. >>> bool(0) False >>> bool(1) True
What’s more, Python will evaluate any representation of 0 to be a False
value:
# Bool interprets 0's regardless of type # Even a negative zero is interpreted as False. >>> bool(0) False >>> bool(0.0) False >>> bool(0j) False >>> bool(-0) False
It is important that Python, along with most other programming languages, 0 is interpreted as False and all non-zero values are evaluated as True. That is, 1, 42, 1010, and even negative values evaluate to True
. Consider the following:
# Bool interprets all non-zero values # as being Truthy >>> bool(1) True >>> bool(2) True >>> bool(42) True >>> bool(10**10) True >>> bool(-42) True
Strings
Determining the truthiness of a string can be a bit strange at first. The key point to remember is that Python’s bool()
function interprets None
, as well as objects with a value of 0
for the __len__
attribute as False. A string is a collection of characters and, as a Python object, has a __len__()
method. Empty strings have a __len__()
value of 0
which is why they evaluate to False
. Consider the following:
# Bool doesn't interpret 0's from strings. # any non-empty string is considered 'True' >>> bool('0') True >>> bool('1') True >>> bool('False') True >>> bool('') False >>> bool(eval('False')) False
All the strings here were evaluated to be True
except the empty string (2nd to the last example) which is to be expected. In the last example, I’ve used the eval()
function (a.k.a. the evil function) to interpret the string a Python expression.
This converts the string to a built-in type and is evaluated by bool()
as a non-string. A little funky, but still a pretty standard issue. What might be less expected is how the bool()
function treats hexadecimal, octal, and binary values. Consider the following:
# bool won't evaluate 0 values as False for # hex, oct, or bin values because they are # represented by Python as strings >>> bool(hex(0)) # 0x0 True >>> bool(oct(0)) # 0o0 True >>> bool(bin(0)) # 0b0 True # If the value are converted to ints however, bool evaluates to False >>> bool(int(hex(0), 16)), bool(int(oct(0), 8)), bool(int(bin(0), 2)) (False, False, False)
Python uses the built-in functions hex, oct, and bin to convert numerical values to different forms of numerical representations. These values are stored as string representations however and therefore evaluate to truthy values via the bool()
function. However, if the values are converted to the numerical values they represent the bool()
function will then evaluate them accordingly.
Note: The pattern we see emerging here is that string values are evaluated literally only after conversion to non-string representations.
Collections
Python’s bool()
value can be used to evaluate whether collections contain any items. Empty collections evaluate to False and all others evaluate to True
. When used to evaluate a collection, Python’s bool()
function is the equivalent of making the statement “this collection contains data.” If there’s anything in there bool()
returns True
. This is underpinned by Python’s data model and the return value of the __len__()
method. Consider the following:
# Evaluate empty collections for truthiness >>> bool(()), bool([]), bool({}) (False, False, False) # Evaluate non-empty collections for truthiness >>> bool((1, 2, 3)), bool(['a', 'b', 'c']), bool({'1': 'a', 2: 'b', 3: 'c'}) (True, True, True) # Evaluate collections containing empty collections >>> bool([(), (), ()]) True # Evaluate each empty item in a collection >>> for item in [(), (), ()]: ... bool(item) False False False
Most of these examples are to be expected, even the looping evaluations are done in the last one. However, in the 2nd to the last example, where a collection of empty collections is evaluated, we see the first real curveball the bool()
function has to offer. Namely, a collection containing empty collections is considered non-empty.
Python represents all data as objects—including an empty collection. This collection has a __len__()
of zero and no members but still exists in memory as a Python object. The reference to this object is considered a non-zero value by Python’s bool()
and therefore evaluates to True
. The objects themselves will still evaluate to False
(since they don’t hold any references) which is shown in the last example above. Weird, but not without explanation.
Objects & Custom Classes
Everything in Python is an object save a few constants. Python’s data model defines all objects to have a __len__ and __bool__ method. The official documentation describes the __len__ method as such:
Called to implement the built-in function len(). Should return the length of the object, an integer >= 0. Also, an object that doesn’t define a __bool__() method and whose __len__() method returns zero is considered to be false in a Boolean context.
I’m quoting from the __len__
portion of the data model documentation because it contextualizes the relationship between __len__
and __bool__
nicely. Without one, Python will fall back to the other. These methods wrap the built-in functions len()
and bool()
to derive the values of an object. For collections, this uses an effective call to len(self)
. Check out the source code for Python’s List object to get a better idea of how that works. Forewarning, it’s in C.
Let’s consider some examples of custom objects to demonstrate how this works with Python’s data model:
class CustomCollection(object): """A custom class for comparing truthiness""" def __init__(self, items: list or dict or tuple or range): """Add a collection of items""" self.items = items # Create an instance without items empty = CustomCollection([]) print(bool(empty)) True # Create an instance with items not_empty = CustomCollection([1, 2, 3]) print(bool(not_empty)) True
In this example, I’ve created a custom class that stores a list
, dict
, tuple
, or range
generator object in the items property. I’ve then created two instances of this custom object, one with items and one without.
When evaluating these objects for truthiness Python’s bool()
method says they’re both True
. This is because Python has no idea that the items attribute is what should be evaluated! There are a few ways to let Python know this is where it should look to consider whether our instances contain items. Here’s another example where I’ve implemented a custom __len__()
method:
class CustomCollection(object): """A custom class for comparing truthiness""" def __init__(self, items: list or dict or tuple or range): """Add a collection of items""" self.items = items def __len__(self): """Returns the number of items in our object""" return len(self.items) # Create an instance without items empty = CustomCollection([]) print(bool(empty)) False # Create an instance with items not_empty = CustomCollection([1, 2, 3]) print(bool(not_empty)) True
Caveats
Python’s bool()
method works as expected in most cases. The examples where eval()
was used can be a bit tricky to predict—but so can using eval()
. Calling bool()
on custom object instances can be tricky too—but that’s no fault of Python. The only exception here would be misunderstanding how Python’s data model derives a boolean value. Let’s consider one final example using another custom class.
class CustomCollection: """A custom class for comparing truthiness""" def __init__(self, items: list or dict or tuple or range): """Add a collection of items""" self.items = items def __len__(self): """Returns the number of items in our object""" return len(self.items) def __bool__(self): """Evaluates the object's truthiness""" return False # Create a new, non-empty object items = CustomCollection([1, 2, 3, 4, 5]) # Check the object has items print(items.__len__()) 5 # Check object truthiness print(bool(items)) False
Here I’ve implemented the __bool__()
method of the CustomCollection object as returning False
no matter what. Python will pass the value of __len__
to bool()
when evaluating for the truthiness of objects if and only if the __bool__ method is not implemented. In cases where the __bool__
method exists it will revert to that value.
In the example above, the __len__()
method sensibly returns the number of objects in the items collection. However, I’ve implemented __bool__
to return False
regardless of our collection size. Several obvious fixes include the following:
return self.__len__() return bool(self.__len__()) return self.__len__() > 0
The caveats shown here are really caveats of how one’s custom object models are implemented—not how the bool()
function works. The takeaway is to know that the bool()
will use the value of __len__
if no __bool__
method is implemented. Do something sensible there and you’ll avoid any issues!
Does Bool() Really Matter?
Python’s bool() method allows one to easily assign a True or False value to a variable that can be conveniently used for control flow via conditional statements. In practice, I rarely find myself needing to use this function and am served by simply adding the expressions explicitly. The only times I find myself using it are those where I’m trying to sweeten my syntax a little bit, albeit usually at the expense of clarity. An example:
import random # Define a lucky number lucky = 3 # Define a list of ints numbers = list(range(10)) # Multiply random ints 10 times to see if # they match the lucky number; print result for i in range(5): print( bool(sum(1 for x in numbers if random.choice(numbers) * x == lucky)) ) # Result: False True False False False
Here I’ve used a one-liner that takes advantage of the bool and sum functions to get compare an aggregate value. I’m checking whether multiplying random ints
from a list
together will ever result in my lucky number 3. Maybe not but it is the best use-case for bool()
that I could come up with quickly that wouldn’t be better expressed explicitly using the constants in
, if
, is
, not
or a standard comparison operator. Is this a sensible thing to be doing? I’ll let readers be the final judge on that.
Final Thoughts
Here we’ve seen the basics of bool(), some less-common uses, and even some edge cases. The bool() function gets made use of heavily via Python’s internal flow of data. However, I find very few use-cases where to use it explicitly. It is useful sometimes in processing large amounts of data or evaluating composite values.
The Python built-in functions are a family of powerful tools that are involved in many underpinnings of the languages lower-level features. I find being aware of their basic functionality helps to optimize my code—even if I’m not using the functions explicitly.