Implicit conversion to boolean in if and while statements

Steven D'Aprano steve+comp.lang.python at pearwood.info
Mon Jul 16 00:03:29 EDT 2012


On Sun, 15 Jul 2012 22:15:13 -0400, Devin Jeanpierre wrote:

> For example, instead of "if stack:" or "if bool(stack):", we could use
> "if stack.isempty():". This line tells us explicitly that stack is a
> container. 

isempty is not a container method.

py> container = []
py> container.isempty()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute 'isempty'

Your code tells us explicitly that stack is expected to be an object with 
an isempty() method. What does that mean? Who knows?

calories = macdonalds.fries('large')
calories.isempty()
=> returns True



When you want to write polymorphic code to handle your stack, you end up 
doing something like this:

if isinstance(stack, MyStackClass):
    flag = stack.isempty()
else: 
    try:
        # list, collections.deque, many others
        flag = len(stack) == 0
    except AttributeError:
        try:
            if sys.version < '3':
                flag = not stack.__nonzero__()
            else:
                flag = not stack.__bool__()
        except AttributeError:
            # Is this even possible in Python 3?
            flag = False  # I guess...
# If we get here, flag is true if stack is empty.
if flag:
    ...

Yeah, explicit is *so much better* for readability. Can't you just *feel* 
how much more readable all those irrelevant implementation details are?

If you're smart, you wrap all of the above in a function:

def isempty(stack):
    # blah blah as above


But if you're *really* smart, you write to the interface and let Python 
take care of the polymorphic details for you:

if not stack:
    ...


(Assuming that stack defines __nonzero__ or __len__ correctly, which it 
better if it claims to be a container.)

It boggles my mind that people who are perfectly happy to program to an 
interface or protocol when it comes to (say) iterables, numbers or even 
big complex classes with dozens of methods, suddenly freak out at the 
thought that you can say "if obj" and obj is duck-typed.

There's a distinct lack of concrete, actual problems from duck-typing 
bools, and a heavy over-abundance of strongly-held opinion that such a 
thing is self-evidently wrong.


> As far as I know, the only use of having a polymorphic boolean
> conversion is reducing the amount of typing we do.

The same could be said about *every* polymorphic function.

The benefit is not just because you don't wear out your keyboard as fast. 
The benefit is the same for all other polymorphic code: it lets you write 
better code faster with fewer bugs and less need for unnecessary type 
restrictions.

If there are a few corner cases where you actually *need* to restrict the 
type of your flags to a actual bool, well, Python gives you the tools to 
do so. Just as you can restrict the type of a sequence to exactly a list 
and nothing else, or a number as exactly a float and nothing else. Just 
do your type tests before you start operating on the object, and reject 
anything that doesn't match what you want.

But that should be the exception, not the rule.


> Generally objects
> with otherwise different interfaces are not interchangeable just because
> they can be converted to booleans, so you wouldn't lose much by being
> forced to explicitly convert to boolean with something
> interface-specific.

Until somebody writes an awesomely fast stack class in C and gives it an 
is_empty() method instead of isempty, and your code can't use it because 
you made unnecessary assumptions about the implementation.



-- 
Steven



More information about the Python-list mailing list