Mastering Objectoriented Python
上QQ阅读APP看书,第一时间看更新

The __bool__() method

Python has a pleasant definition of falsity. The reference manual lists a large number of values that will test as equivalent to False. This includes things such as False, 0, '', (), [], and {}. Most other objects will test as equivalent to True.

Often, we'll want to check for an object being "not empty" with a simple statement as follows:

if some_object:
    process( some_object )

Under the hood, this is the job of the bool() built-in function. This function depends on the __bool__() method of a given object.

The default __bool__() method returns True. We can see this with the following code:

>>> x = object()
>>> bool(x)
True

For most classes, this is perfectly valid. Most objects are not expected to be False. For collections, however, this is not appropriate. An empty collection should be equivalent to False. A nonempty collection can return True. We might want to add a method like this to our Deck objects.

If we're wrapping a list, we might have something as shown in the following code snippet:

def __bool__( self ):
    return bool( self._cards )

This delegates the Boolean function to the internal _cards collection.

If we're extending a list, we might have something as follows:

def __bool__( self ):
    return super().__bool__( self )

This delegates to the superclass definition of the __bool__() function.

In both cases, we're specifically delegating the Boolean test. In the wrap case, we're delegating to the collection. In the extend case, we're delegating to the superclass. Either way, wrap or extend, an empty collection will be False. This will give us a way to see whether the Deck object has been entirely dealt and is empty.

We can do things as shown in the following code snippet:

d = Deck()
while d:
    card= d.pop()
    # process the card

This loop will deal all the cards without getting an IndexError exception when the deck has been exhausted.