Boolean tests [was Re: Attack a sacred Python Cow]

Anders J. Munch 2007 at jmunch.dk
Mon Jul 28 19:19:00 EDT 2008


Steven D'Aprano wrote:
> On Sun, 27 Jul 2008 23:45:26 -0700, Carl Banks wrote:
> 
>> I want something where "if x" will do but a simple explicit test won't.
> 
> Explicit tests aren't simple unless you know what type x is. 

If you don't even know a duck-type for x, you have no business invoking any 
methods on that object.

If you do know a duck-type for x, then you also know which explicit test to perform.

> Explicit tests are not necessarily simple for custom classes. Testing for 
> emptiness could be arbitrarily complex. That's why we have __nonzero__, 
> so you don't have to fill your code with complex expressions like (say)
> 
> if len(x.method()[x.attribute]) > -1
> 
> Instead you write it once, in the __nonzero__ method, and never need to 
> think about it again.

Okay, so you have this interesting object property that you often need to test 
for, so you wrap the code for the test up in a method, because that way you only 
need to write the complex formula once.  I'm with you so far.  But then you 
decide to name the method "__nonzero__", instead of some nice descriptive name? 
  What's up with that?

This is the kind of code I would write:
    class C:
       def attribute_is_nonnegative(self):
          return len(self.method()[self.attribute]) > -1
    ...
    c = get_a_C()
    if c.attribute_is_nonnegative():
       ...

Now suppose you were reading these last few lines and got to wondering if 
get_a_C might ever return None.

The answer is obviously no.  get_a_C must always return a C object or something 
compatible.  If not, it's a bug and an AttributeError will ensue.  The code 
tells you that.  By giving the method a name the intent of the test is perfectly 
documented.

In comparison, I gather you would write something like this:
    class C:
       def __nonzero__(self):
          return len(self.method()[self.attribute]) > -1
    ...
    c = get_a_C()
    if c:
       ...

Again, the question is, can get_a_C return None?  Well that's hard to say 
really.  It could be that "if c" is intended to test for None.  Or it could be 
intended to call C.__nonzero__.  Or it could be cleverly intended to test 
not-None and C.__nonzero__ at the same time.  It may be impossible to discern 
the writer's true intent.

Even if we find out that C.__nonzero__ is called, what was it that __nonzero__ 
did again?  Did it test for the queue being non-empty?  Did it test for the 
queue being not-full?  Did it test whether for the consumer thread is running? 
Did it test for if there are any threads blocked on the queue?  Better dig up 
the class C documentation and find out, because there is no single obvious 
interpretation of what is means for an object to evaluate to true.

"if x" is simple to type, but not so simple to read. "if x.namedPredicate()" is 
harder to type, but easier to read.  I prefer the latter because code is read 
more often than it is written.

regards,
Anders



More information about the Python-list mailing list