Boolean Tests: __bool__ and __len__

The next set of methods is truly useful (yes, pun intended!). As we’ve learned, every object is inherently true or false in Python. When you code classes, you can define what this means for your objects by coding methods that give the True or False values of instances on request. The names of these methods differ per Python line; this section starts with the 3.X story, then shows 2.X’s equivalent.

As mentioned briefly earlier, in Boolean contexts, Python first tries __bool__ to obtain a direct Boolean value; if that method is missing, Python tries __len__ to infer a truth value from the object’s length. The first of these generally uses object state or other information to produce a Boolean result. In 3.X:

>>> class Truth:
       def __bool__(self): return True

>>> X = Truth()
>>> if X: print('yes!')

yes!

>>> class Truth:
       def __bool__(self): return False

>>> X = Truth()
>>> bool(X)
False

If this method is missing, Python falls back on length because a nonempty object is considered true (i.e., a nonzero length is taken to mean the object is true, and a zero length means it is false):

>>> class Truth:
       def __len__(self): return 0

>>> X = Truth()
>>> if not X: print('no!')

no!

If both methods are present Python prefers __bool__ over __len__, because it is more specific:

>>> class Truth:
       def __bool__(self): return True            # 3.X tries __bool__ first
       def __len__(self): return 0                # 2.X tries __len__ first

>>> X = Truth()
>>> if X: print('yes!')

yes!

If neither truth method is defined, the object is vacuously considered true (though any potential implications for more metaphysically inclined readers are strictly coincidental):

>>> class Truth:
        pass

>>> X = Truth()
>>> bool(X)
True

At least that’s the Truth in 3.X. These examples won’t generate exceptions in 2.X, but some of their results there may look a bit odd (and trigger an existential crisis or two) unless you read the next section.

Boolean Methods in Python 2.X

Alas, it’s not nearly as dramatic as billed—Python 2.X users simply use __nonzero__ instead of __bool__ in all of the preceding section’s code. Python 3.X renamed the 2.X __nonzero__ method to __bool__, but Boolean tests work the same otherwise; both 3.X and 2.X use __len__ as a fallback.

Subtly, if you don’t use the 2.X name, the first test in the prior section will work the same for you anyhow, but only because __bool__ is not recognized as a special method name in 2.X, and objects are considered true by default! To witness this version difference live, you need to return False:

C:\code> c:\python33\python
>>> class C:
        def __bool__(self):
            print('in bool')
            return False

>>> X = C()
>>> bool(X)
in bool
False
>>> if X: print(99)

in bool

This works as advertised in 3.X. In 2.X, though, __bool__ is ignored and the object is always considered true by default:

C:\code> c:\python27\python
>>> class C:
        def __bool__(self):
            print('in bool')
            return False

>>> X = C()
>>> bool(X)
True
>>> if X: print(99)

99

The short story here: in 2.X, use __nonzero__ for Boolean values, or return 0 from the __len__ fallback method to designate false:

C:\code> c:\python27\python
>>> class C:
        def __nonzero__(self):
            print('in nonzero')
            return False                 # Returns int (or True/False, same as 1/0)

>>> X = C()
>>> bool(X)
in nonzero
False
>>> if X: print(99)

in nonzero

But keep in mind that __nonzero__ works in 2.X only; if used in 3.X it will be silently ignored and the object will be classified as true by default—just like using 3.X’s __bool__ in 2.X!

And now that we’ve managed to cross over into the realm of philosophy, let’s move on to look at one last overloading context: object demise.