Python bool(): The Syntax of Truth

python bool function

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;DRbool() 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 inifis, 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.

alpharithms discord banner 1
Zαck West
Entrepreneur, programmer, designer, and lifelong learner. Can be found taking notes from Mother Nature when not hammering away at the keyboard.