4

The style guide reads:

# Correct:
if greeting:

# Wrong:
if greeting == True:

# Worse:
if greeting is True:

See PEP 8, search for the word "worse"

Why is this? I am accustomed to checking conditions as explicit as possible, to make the code more readable, and to catch aberrations.

Consider this function:

def f(do_it):
   if do_it : print("doit")
   else : print("no don't") 

It is easy to abuse/oversee, with unexpected behaviour

>>> f("False")
doit
>>> f([False])
doit

This is a real problem when, for instance, you are checking a return value that could unintentionally pass an if clause. This could be avoided by using the is construct.

Clearly there's a good reason for the PEP recommendation, but what is it?

Further research, prompted by the commenters, lead me to the following findings:

if x:

invokes the __bool method of the class of x. The method should return True or False depending on which of the two the object deems itself to be.

if x==True:

invokes the __eq method of the class of x. The method should be able to compare itself to True (or False), and return True or False, as the case may be.

if x is True

invokes neither. This tests whether x is the "True" object. It completely circumvents the __eq and __bool methods.

Note: I am not asking about the difference between == and is. If that's why you are here, see Is there a difference between "==" and "is"?

P2000
  • 1,042
  • 8
  • 14
  • Programming languages have mostly gotten this wrong forever. You should never have been able to test anything that is not either already boolean or via an explicit boolean condition, in my opinion. Unrelated, single line `if` and `else` statements are broadly discouraged. – jarmod Jun 09 '20 at 21:58
  • Comparing a `bool` to `True` is redundant and misleading. It says "this may be `True`, or it may be `False`, or it may be something else". It's *already* a `bool`. You don't need to keep comparing it to `True`. If `x` is a `bool`, then the following are equivalent: `if x:`, `if x == True:`, `if (x == True) == True:`, `if ((x == True) == True) == True:`, etc. Only one of these is free of useless, misleading garbage: `if x:`. – Tom Karzes Jun 09 '20 at 21:59
  • You are seemingly confusing explicitness (good) with pointless verbosity (bad). `x == True` isn’t any more explicit than `x`. It is just pointlessly verbose. As for using `is` here, there’s simply *no positive reason* to do so, neither explicitness nor otherwise. On the contrary, you’re semantically incorrectly using reference semantics when you’re testing a *value*. Regarding your example, if you want avoid type errors, use type annotations and a type checker. – Konrad Rudolph Jun 09 '20 at 22:00
  • @jarmod: That requires a statically typed language, or explicit comparison syntax in every `if` and `while`. – user2357112 Jun 09 '20 at 22:22
  • @user2357112 Nonsense, the language could simply forbid it and fail at runtime when a condition does not evaluate to a value of type `bool`. Something equivalent already happens, just more permissively. This is a conscious decision, not merely a side-effect of dynamic typing. – Konrad Rudolph Jun 09 '20 at 22:24
  • @KonradRudolph: True. I was thinking about statically forbidding it, perhaps since the only languages I'm familiar with that forbid it do so statically. – user2357112 Jun 09 '20 at 22:31

2 Answers2

5

Why is this?

Because it’s logically wrong, a category mistake. With is you are explicitly performing an identity check, a reference comparison. But that’s not what you intend to do here. The intent of the code is to check if a value is truthy (or, more strictly, True). Whether that value happens to reside at the same address in memory as the constant True is not only irrelevant, it’s actively distracting.

In other words, the intent of the code concerns the value of the expression, so inspect its value, not its reference identity.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
2

On a theoretical level, is expresses the wrong thing. We care about the truthiness of the value, not the identity. On the rare cases where we do care about the identity, an is comparison is appropriate.

On a practical level, is True doesn't even work the way people expect it to:

In [1]: import numpy

In [2]: x = numpy.array([1, 2, 3])

In [3]: flag = x[0] == 1

In [4]: flag
Out[4]: True

In [5]: if flag: print('true')
true

In [6]: if flag is True: print('true')

In [7]:

We compared a 1 to a 1, got a thing that looks like True, but the is comparison failed. That's because bool isn't the only boolean type. Libraries are free to define their own. flag is an instance of numpy.bool_, and it's a different object from True. (NumPy has a good reason for this - using their own boolean type allows them to provide more uniform handling of 0-dimensional values. This is the same reason NumPy also has its own numeric scalar types.)

Also, is True doesn't even catch the problem in your example. It just switches one silent misbehavior for another. f("False") printing doit is a problem, but so is f("True") silently doing nothing. Neither version of the test produces an actual error message.

user2357112
  • 260,549
  • 28
  • 431
  • 505
  • "because bool isn't the only boolean type" that's the clincher. Also, "is" does not invoke __eq__ (used for x==True) or __bool__ (used for "if x"), so it is indeed worse. – P2000 Jun 10 '20 at 18:29