Operator Precedence/Boolean Logic

Steven D'Aprano steve+comp.lang.python at pearwood.info
Thu Jun 23 05:39:23 EDT 2016


On Thursday 23 June 2016 17:58, Antoon Pardon wrote:

> Op 23-06-16 om 05:59 schreef Steven D'Aprano:
>> On Thu, 23 Jun 2016 01:12 pm, Larry Hudson wrote:
>>
>>> On 06/22/2016 12:42 AM, Lawrence D’Oliveiro wrote:
>>> [snip]
>>>> I feel that’s a needlessly complicated rule. It would have been simpler
>>>> if boolean operators (and conditional expressions like in if-statements
>>>> and while-statements) only allowed values of boolean types. But that’s
>>>> one of the few warts in the design of Python...
>>>>
>>> Wart??  I *strongly* disagree.  I find it one of the strengths of Python,
>>> it enhances Python's
>>> expressiveness.  Of course, everyone is entitled to their own
>>> opinion...and this is mine.
>> Allowing any value as a truth value is just applying the principle of
>> duck-typing to booleans.
> 
> What does that mean? As far as I understood, duck typing was that you
> could define any class with the same attributes and methods as an other,
> often a built in, at which point you could substitute instance of this
> new class anywhere you originally expected instance of the old class.

But you only need to implement the behaviour you need, not the entire Duck 
interface. If you only need quack(), you don't need to implement swim(), or 
provide an actual Duck, any object capable of quacking will do.

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:

if the object defines __len__:
   if it returns 0, then the object is false;
   else the object is true
elif the object defines __nonzero__ (__bool__ in Python 3)
    if it returns a true value, then the object is true
    else the object is false
else
    # neither __len__ nor __nonzero__ is defined
    the object is true


> My experience is that this doesn't work with booleans. When I need
> real booleans, encountering whatever else that can act like a boolean,
> is more often than not an indication something went wrong but the
> detection of it going wrong is delayed, because almost everything
> can act like a boolean. It is why I have sometime found the need
> to write:
> 
>     if flag is True:
> 
> Because flag needed to be True, not truthy.

I find this hard to believe. Why do you care if somebody passes you 1 or "T" as 
the flag instead of True? And if they do pass something other than True, you 
don't get an exception, you simply take the false branch.

Somehow I doubt that you write three-state logic everywhere:

if flag is True: ...
elif flag is False: ...
else: ...


By the way, it is a particular error of folks using so-called "real bools" to 
compare something you already know is a bool against True. I used to see it all 
the time in my Pascal days, where people would define a boolean flag then write 
"if flag == True". Given:

assert isinstance(flag, bool)

then writing 

    flag is True

returns a bool. So how do you test a bool? By comparing it to True:

    flag is True is True

But that also returns a bool, so you must enter an infinite regress:

    flag is True is True is True is True... # help me, where do I stop???

The right answer, of course, is to stop *right at the beginning*. Stopping at 
Step 2 is silly and pointless. Since you know flag is a True or False value, 
then you just write:

    flag

And the same applies to true and false values.




-- 
Steve




More information about the Python-list mailing list