question about True values

Steven D'Aprano steve at REMOVE.THIS.cybersource.com.au
Sat Oct 28 18:10:59 EDT 2006


On Sat, 28 Oct 2006 03:24:50 -0700, Carl Banks wrote:

> Not all objects that have a state of emptiness consider emptiness to be
> false.

In which case they should define __nonzero__ appropriately.

In which case, calling code that assumes that len(obj) is a substitute for
truth-testing will do the wrong thing.


>> > Whether you test with "if a:" or "if len(a)>0", some objects are
>> > going to be denied.
>>
>> If a class doesn't define __nonzero__ or __len__, it should.
> 
> No, it often shouldn't.

Okay, I want to qualify my statement: if a class doesn't define
__nonzero__ or __len__, *and doesn't want the default Python behaviour of
all instances evaluating as True*, then they should.

> A. It's not always desirable for empty to be false.  Numpy defines a
> bunch of numeric array types that raise an exception when __nonzero__
> (actually nb_nonzero in the C API) is called.  This is the correct
> behavior for numpy.  It makes no sense for numpy arrays to be used in a
> boolean context, but they certainly can be empty.

And, appropriate to the class, numpy arrays raise an exception when
__nonzero__ is called -- just as they should.



> B. Sometimes it's impossible to determine the state of emptiness.  For
> example, iterators.

Since Guido has ruled that the protocol is that all iterators are True,
there is no need for __nonzero__ since the default behaviour does the job.


[snip]
>> Perhaps this behaviour has changed in version 2.5, if so, I'd like to
>> hear the rationalisation before I declare it a mistake.
> 
> You haven't been paying attention.
> 
> Yes, this behavior has been changed in 2.5.  The built-in iterators
> always return True in 2.5.  But even in 2.4, it was not true for
> iterators in general:

Yes, you are right. I was fooled by a coincidence.

> At no point could you count on an iterator returning False for empty,
> unless you'd made it yourself.  Neither "if a:" nor "if len(a)>0" is a
> valid test for an empty iterator.

Correct. Guido's decision is that iterators are always "Something" (that
is, True in a truth context) even if they are exhausted.


>> > P.S. binary trees do have length: it's the number of nodes, just as
>> > the number of keys is the length of a dict.
>>
>> I don't know what you were taught, but I was taught that binary trees
>> have height and breadth.
> 
> It doesn't really matter.  dicts and sets don't have a length, either.
> Or if they do, it's the length of the hash table, not the number of
> entries.  Weren't you taught that?  But Python uses len() to get the
> number of items in a container.  Any Pythonic implementation of a binary
> tree class would use len() to return the number of entries in it.
> Anything that uses indexing ought to define len(), if it can.

In the binary tree:

tree -->  A
A.left --> B, A.right --> C

what's tree[0]? Should it be A (preorder), or B (inorder) or C (postorder)?


> Overall, your objections don't really apply, since you're arguing what
> ought to be whereas my argument is pragmatic.  Practically speaking, in
> realistic situations, "if len(a)>0" will work for a wider range of types
> than "if a:".

Well, that's a quantitative claim you're making there. Have you
actually gone through, say, the built in types and checked how many
have a length versus how many work in a truth-context?

>>> import types
>>> if types:
...     print "Modules work with bool"
...
Modules work with bool
>>> if len(types)>0:
...     print "Modules work with len"
...
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: len() of unsized object

"if len(a)>0:" only works for objects that define __len__. 

"if a:" works for any object that doesn't go to the trouble of
specifically prohibiting it, as numpy arrays deliberately do. That's the
Python way, and it is a deliberate design.


-- 
Steven.




More information about the Python-list mailing list