Python Boolean Operations Yield Truthiness, Not Truth

· 413 words · 2 minute read

Python’s boolean operations and and or do some things that I never fully comprehended before now.

In the way that I’ve modeled these operations in my head, the operation always yielded True or False. For example, if I were doing a conditional statement, and I provided a falsey statement, such as None or an empty string, I know that that will evaluate to False:

# None and "" are both Falsey
if None or "":
    # This will never evaluate
    print("you can't see me!")

And providing a conditional with a truthy statement will evaluate to True:

# "abc" is truthy
if None or "abc":
    # This evaluates!
    print("hello, world!")

Today, though, my eyes were opened to the fact that these statements evaluate the truthiness of the value, and yield the value.

$ python
Python 3.12.5 (main, Aug 14 2024, 05:08:31) [Clang 18.1.8 ] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> None or ""
''
>>> None or "abc"
'abc'
>>>

My previous mental model expected the result of the first statement to be False, and the second statement to be True.

By truthiness, I’ll quote a snippet of the python documentation on Boolean operations:

In the context of Boolean operations, and also when expressions are used by control flow statements, the following values are interpreted as false: False, None, numeric zero of all types, and empty strings and containers (including strings, tuples, lists, dictionaries, sets and frozensets). All other values are interpreted as true. User-defined objects can customize their truth value by providing a __bool__() method.

Here’s the part of the documentation that explains the behavior I was not expecting:

The expression x or y first evaluates x; if x is true, its value is returned; otherwise, y is evaluated and the resulting value is returned.

Note that neither and nor or restrict the value and type they return to False and True, but rather return the last evaluated argument. This is sometimes useful, e.g., if s is a string that should be replaced by a default value if it is empty, the expression s or ‘foo’ yields the desired value. Because not has to create a new value, it returns a boolean value regardless of the type of its argument (for example, not ‘foo’ produces False rather than ‘’.)

That last sentence describing not is how I thought and and or worked too

  • I had thought it created a new boolean value, but that’s not the case.