Learning Python - Mark Lutz [400]
* * *
Boolean Tests: __bool__ and __len__
As mentioned earlier, classes may also define methods that give the Boolean nature of their instances—in Boolean contexts, Python first tries __bool__ to obtain a direct Boolean value and then, if that’s missing, tries __len__ to determine a truth value from the object length. The first of these generally uses object state or other information to produce a Boolean result:
>>> 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.0 tries __bool__ first
... def __len__(self): return 0 # 2.6 tries __len__ first
...
>>> X = Truth()
>>> if X: print('yes!')
...
yes!
If neither truth method is defined, the object is vacuously considered true (which has potential implications for metaphysically inclined readers!):
>>> class Truth:
... pass
...
>>> X = Truth()
>>> bool(X)
True
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.
* * *
Booleans in Python 2.6
Python 2.6 users should use __nonzero__ instead of __bool__ in all of the code in the section Boolean Tests: __bool__ and __len__. Python 3.0 renamed the 2.6 __nonzero__ method to __bool__, but Boolean tests work the same otherwise (both 3.0 and 2.6 use __len__ as a fallback).
If you don’t use the 2.6 name, the very first test in this section will work the same for you anyhow, but only because __bool__ is not recognized as a special method name in 2.6, and objects are considered true by default!
To witness this version difference live, you need to return False:
C:\misc> c:\python30\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.0. In 2.6, though, __bool__ is ignored and the object is always considered true:
C:\misc> c:\python26\python
>>> class C:
... def __bool__(self):
... print('in bool')
... return False
...
>>> X = C()
>>> bool(X)
True
>>> if X: print(99)
...
99
In 2.6, use __nonzero__ for Boolean values (or return 0 from the __len__ fallback method to designate false):
C:\misc> c:\python26\python
>>> class C:
... def __nonzero__(self):
... print('in nonzero')
... return False
...
>>> X = C()
>>> bool(X)
in nonzero
False
>>> if X: print(99)
...
in nonzero
But keep in mind that __nonzero__ works in 2.6 only; if used in 3.0 it will be silently ignored and the object will be classified as true by default—just like using __bool__ in 2.6!
* * *
Object Destruction: __del__
We’ve seen how the __init__ constructor is called whenever an instance is generated. Its counterpart, the destructor method __del__, is run automatically when an instance’s space is being reclaimed (i.e., at “garbage collection” time):
>>> class Life:
... def __init__(self, name='unknown'):
... print('Hello', name)
... self.name = name
... def __del__(self):
... print('Goodbye', self.name)
...
>>> brian = Life('Brian')
Hello Brian
>>> brian = 'loretta'
Goodbye Brian
Here, when brian is assigned a string, we lose the last reference to the Life instance and so trigger its destructor method. This works, and it may be useful for implementing some cleanup activities (such as terminating server connections). However, destructors are not as commonly used in Python as in some OOP languages, for a number of reasons.
For one