Implicit conversion to boolean in if and while statements

Steven D'Aprano steve+comp.lang.python at pearwood.info
Mon Jul 16 20:43:47 EDT 2012


On Mon, 16 Jul 2012 13:54:32 -0700, Ethan Furman wrote:

> Andrew Berg wrote:
>> On 7/15/2012 9:38 PM, Steven D'Aprano wrote:
>>>> I would expect None to mean "doesn't exist" or "unknown" or something
>>>> like that - e.g., a value of 0 means 0 jelly beans in the jar and
>>>> None means there isn't a jar.
>  >>
>>> How you interpret some_variable = None depends on what some_variable
>>> represents. If some_variable represents "number of jelly beans in a
>>> jar", then that should be 0 if there is no jar.
>  >
>> What is None supposed to mean then, and what should I do when I have to
>> make a distinction between "doesn't exist" and "empty"? Sure, if I need
>> to count the total number of jelly beans in all my stores, the
>> distinction is meaningless, but if I need to find out which stores sell
>> jelly beans so I know which stores need to be restocked, the
>> distinction is quite important.
> 
> I'm not sure what Steven was trying to say there, but for me:
> 
> jar with no jellybeans == 0
> 
> no jar == None

The existence of a jar or no jar is irrelevant to the question of how 
many jellybeans there are. They are two different things, and therefore 
need two different values. There are many ways to implement this.

# One way
jar_exists = True  # or possibly False
jellybeans = 42  # or whatever the number is, possibly 0

# Another way
jar = Jar(number_of_beans=jellybeans) if jar_exists else None
jellybeans = jar.jellybeans if jar is not None else 0

If you have many jars, and you want to count the total number of 
jellybeans:

total = sum(jar.jellybeans for jar in jars if jar is not None)


Strictly speaking, the "is not None" is redundant, but it expresses the 
intent better than the alternative. Assuming that jar instances follow 
the standard Python API for containers, and is treated as falsey when it 
has a jellybean count of zero:

total = sum(jar.jellybeans for jar in jars if jar)
  # relies on the fact that adding zero to a number makes 
  # no difference, so you can safely leave zeroes out


Here's a case where you *must* distinguish between empty jars and None:

number_of_jars = sum(1 for jar in jars if jar is not None)

and a case where you *shouldn't*:

number_of_nonempty_jars = sum(1 for jar in jars if jar)

Of course you can write this:

number_of_nonempty_jars = sum(
    1 for jar in jars if jar is not None and jar.jellybeans > 1
    )

but that overwhelms the code with incidental implementation details about 
jellybean counts, which is prone to bugs. (Did you spot it?)

Even jar.isempty() would be better, but better still is to *not* invent 
your own container API but to use the standard Python one instead and 
define an appropriate __nonzero__ (Python 2) or __bool__ (Python 3) 
method.

If I insist on making a single object do duty for both the jar and the 
jellybean count, then I need a "null jar object", and I probably end up 
with something like this:

Jar(number_of_beans=None) => null jar object with jar.jellybeans = 0
Jar(number_of_beans=0) => normal jar object with jar.jellybeans = 0
Jar(number_of_beans=42) => normal jar object with jar.jellybeans = 42

and then my code becomes even more complicated and less understandable, 
but hey, it's *my* code and I can do whatever damn-fool thing I like!


-- 
Steven



More information about the Python-list mailing list