Operator Precedence/Boolean Logic

Steven D'Aprano steve at pearwood.info
Thu Jun 23 08:37:41 EDT 2016

On Thu, 23 Jun 2016 08:21 pm, Antoon Pardon wrote:

> Op 23-06-16 om 11:39 schreef Steven D'Aprano:
>> To decide on a branch, you don't need an actual bool, anything capable of
>> acting like a bool will do. As a language design, ALL objects are capable
>> of acting like a bool. Python has a specific protocol in place for
>> deciding whether an object quacks like a bool:
> But an object acting like a bool, doesn't mean this bool behaviour makes
> the disctinction you actually need. 

Um... okay?

I'm not really sure I understand the point you think your making. Lists and
tuples can be exchanged in many duck-typing situations, but if you need
something with an in-place sort method, you can't use a tuple. That's a
given. If you need some feature that isn't offered by truthiness, you can't
use truthiness to detect that feature. It seems hardly worth mentioning.

> Python tempts people to rely on those 
> truthy values, because it saves typing is pythonic and it works for the
> moment and then something unexpected gets passed through and you find
> yourself chasing a very illusive bug.

You keep saying that, but I've never seen it happen. I've seen cases where
people have been surprised by the unexpected truthiness of an object ("I
expected an exhausted iterator to be false, but it was true"), but that's
not what you seem to be describing.

To be honest, I'm not sure what you are describing. Your explanation is
maddeningly vague. Since bools offer a *very small* API (beyond what they
also offer as a subclass of int), I cannot imagine what unexpected thing
you might get passed in that leads to "a very illusive bug".

The bottom line is, bools offer only a single major API[1]: are they truthy,
or falsey? What else can do you with them? Nothing. They support
conditional branches, and boolean operators "or" and "and". That's all.
They don't have methods to call, or attributes to set. You can use a bool
to take the if branch, or the else branch, and that's effectively all.

Since *all* objects support that same bool API, what can you *possibly* be
passed in place of a bool that fails to work?

I acknowledge, cheerfully, that there might be a mismatch between what you
expect and what the object actually does. "I expect empty containers to be
falsey, and non-empty ones to be truthy; but this RedBlackTree object is
always false even if it has many items; and this SortedDict is always true
even when it is empty."

That's a nuisance and a pain when it happens, but it's arguably a bug in the
object or at least a misfeature, and besides that's the risk when you
duck-type. You're trusting the object that you get to behave like the
object you expect, or it will break things.

(An object with a misbehaving __bool__ is no better or worse than an object
with a misbehaving sort() method, or __add__ method.)

So I'm not really sure what you are trying to describe. I guess it might be
something like this:

def spam(alist):
    if alist:
        print("empty list")

If you pass 1 instead of an actual list, then you don't get an error until
somewhere inside process(). Potentially far, far away. So you want to

def spam(alist):
    if len(alist):
        process(alist)  # requires an actual list
        print("empty list")

and now calling spam(1) will raise immediately. Great.

But that's not really anything to do with *bools* specifically. If you call
spam() with a dict, or a set, or a tuple, or a RedBlackTree, any other
object with a length, you're no better off. If you absolutely need a list,
and nothing else, then you have to type-check.

In practice, I don't see how truthy/falsey objects lead to more or worse
bugs than any other form of dynamic typing.

To me, your position is equivalent to saying that dynamic typing and
duck-typing is really great, except for len(). len() should absolutely only
work on lists, and nothing else, so any time you want to get the length of
an object, you must work with real lists, not listy sequences:

if len(list(mystring)) > 20: ...

print(len(list(mydict.keys())), "items")

etc. Substitute "bool" for "len" and you might understand how I feel about
your position.

>> Somehow I doubt that you write three-state logic everywhere:
> Since I wrote above the I *sometimes* found this need. You may
> safely assume I don't write it everywhere.

Fair enough.

[1] I exclude minor things like repr(True), and the fact that they are
subclassed from int.


More information about the Python-list mailing list