5

In /tmp/spam.py:

n = 69

if n == True:
    print 'potato'

pep8 utility complains about this conditional:

wim@SDFA100461C:/tmp$ pep8 spam.py 
spam.py:3:6: E712 comparison to True should be 'if cond is True:' or 'if cond:'
  • the first suggestion is wrong/"worse" according to pep8 itself
  • the second suggestion changes the behaviour of the code

What is the best practice for a case where you actually do want to check equality with True? Is identity checking with True using is OK? Why does pep8 utility offer an alternative which is explicitly discouraged in pep8 itself?

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
wim
  • 338,267
  • 99
  • 616
  • 750
  • Why would you check an integer variable, to see if it's a boolean? – aIKid Jan 09 '14 at 12:12
  • 1
    Why do you need to explicitly test for `True` values? If you **do** have to test for `True` **only**, then use `is True`. – Martijn Pieters Jan 09 '14 at 12:12
  • 3
    FWIW, PEP 8 does say "Don't compare boolean values to True or False[...]", and what you have isn't a boolean value. Although... yeah, it's a bit odd that the tool suggests `is` when the PEP says it's worse. Your last question should probably be asked of the developer of the tool, though. – Wooble Jan 09 '14 at 12:29

4 Answers4

8

If you really do need to check equality with True then use == and ignore PEP8, but in almost any conceivable case that isn't what you want.

If you want to know whether the value you have is one of the values Python considers to be true, use if cond:. If you want to know whether the value you have is the singleton value True then use is True, the booleans True and False are singletons so it is correct to use is in this situation.

If you need to test whether your object is the singleton True, but a linter or a code reviewer complains about is True, then isinstance(x, bool) and x is a behaviorally equivalent (but slower) substitute.

Checking x == True is a halfway house. It is true when x is True is true, and false for your case of x=69, but there are other objects which are not themselves True but for which x==True gives an unexpectedly true result such as 1 == True being true. (thanks @Ant).

So putting that together:

value of n:    True  1     69     False   0
-----------------------------------------------
expression     result
-----------------------------------------------
if n:          True  True  True   False   False
if n is True:  True  False False  False   False
if n==True:    True  True  False  False   False

Pick the row from that table which gives the results you really wanted (and the last one isn't it).

user2357112
  • 260,549
  • 28
  • 431
  • 505
Duncan
  • 92,073
  • 11
  • 122
  • 156
2

Whatever you do, I think a comment is required since the code looks funny.

If you want to check equality with True then it might be clearer to write if n == 1. Someone reading the code is less likely to misinterpret it as an attempt to test logical truth.

Of course if n has a user-defined type it's possible to define n.__eq__ such that (n == True) != (n == 1), but it would be pretty nasty. So you'd have to decide whether the subtle difference in meaning is justified by the benefit of avoiding the code looking like an incorrect logical truth test.

If the difference is not justified, and if you're absolutely required to write to the PEP8 style guide, then either use assertEqual or write expected_value = True, if n == expected_value.

If the code is a unit test for an API that for some reason defines specifically that the literal True is returned, then of course you should test if n is True, not if n == True. As before, a comment or some indirection would be needed to stop the code looking wrong.

Another option is to change the API you're testing. The reason for the style guide rules is in part to discourage people from inventing or relying on APIs that specifically define that the return value must be identical or equal to the literal True, and instead define APIs and write their code in terms of logical truth. So if you "fix" the API you can "fix" the code that tests it.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
1

Why do you need to explicitly test for True values? You rarely, if ever do need narrow your test down to a specific type. I'd rethink your use case here; if you are building an API that'll return a bool instead of in int for out-of-band conditions, then use exceptions instead.

If you do have to test for True only, then use

if n is True:

because the boolean values are meant to be singletons like None. See the Programming recommendations section:

Comparisons to singletons like None should always be done with is or is not, never the equality operators.

Moreover, because issubtype(bool, int) is true (for historical reasons; bool was introduced rather late in Python), n == True is also true for n = 1, if you can only accept True here then you can only use is True. You could also use isinstance(n, bool) and n, which would allow for subclasses of bool, but I cannot imagine there ever be a use for such a type, and in current implementations, bool explicitly prohibits being subclassed.

The PEP 8 rule about using not using if cond is True: is specifically called out because it limits the value of cond to the bool only.

Last but not least, PEP 8 starts with this:

A Foolish Consistency is the Hobgoblin of Little Minds

[...] But most importantly: know when to be inconsistent -- sometimes the style guide just doesn't apply. When in doubt, use your best judgment. Look at other examples and decide what looks best.

Only follow PEP 8 if it suits your needs.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • 1
    I don't feel like digging up the mailing list message at the moment, but Guido has specifically rejected this construction (in the context of a request to remove the prohibition from PEP 8). His suggestion was `x and isinstance(x, bool)`, which a further caveat that if you're writing this code the API that gave you `x` is horrible and you should fix that instead. – Wooble Jan 09 '14 at 12:18
  • @Wooble: I find that to be worse; is the expectation that there will be more than *one* copy of `True`? Python 3 made `True` and `False` keywords so you cannot assign other values to the two names, further cementing the fact that they are singletons. – Martijn Pieters Jan 09 '14 at 12:19
  • @Wooble: ah, I suppose you could *subclass* `bool` and provide more values. I find the suggestion that a `bool` subclass could be useful somewhat unlikely; better fix the API that produced that idea.. – Martijn Pieters Jan 09 '14 at 12:24
  • 1
    That's not the rationale. `if x is True:` is guaranteed to work correctly in the language specification. The reason it's bad style is that when anyone writes that it's nearly always a bug, and someone writing the code may assume they meant `if x:` and then "fix" it. I'm not sure this is actually a good justification for forbidding it (any more than I'm sure about not allowing assignment in a conditional because people frequently mix up `=` and `==` in languages where it's allowed), but, as you say, it's a style guide and if you're not writing for the stdlib ignore it. :) – Wooble Jan 09 '14 at 12:24
  • For context, I came across this during unit testing of an API where some code is supposed to return the literal `True` and this is being checked with `==` – wim Jan 09 '14 at 12:27
  • @wim: in that case assert that the type returned is `bool` and be done with it. `assertTrue(n)` and `assertTrue(isinstance(n, bool))`. – Martijn Pieters Jan 09 '14 at 12:28
  • OK. Are True and False singletons in both python 2 and 3, out of interest? – wim Jan 09 '14 at 12:29
  • @wim: the [datamodel](http://docs.python.org/2/reference/datamodel.html#the-standard-type-hierarchy) documents them as such. – Martijn Pieters Jan 09 '14 at 12:30
  • @wim: And in Python 3, [`True`, `False` and `None` became reserved keywords](http://docs.python.org/3/whatsnew/3.0.html#changed-syntax) to further protect their singleton status. In Python 2, you can still do `True = 1` and get away with it, mostly. – Martijn Pieters Jan 09 '14 at 12:32
  • @wim: But if you came across it in a unittest, why wasn't that test using `self.assertEqual(n, True)` instead of `n == True`? – Martijn Pieters Jan 09 '14 at 12:34
  • You can't actually subclass `bool` (at least in CPython - I don't know about other implementations). It's not flagged as an acceptable base type, and you'll get a TypeError if you try. – user2357112 Jan 15 '19 at 19:06
  • @user2357112: ah, yes, of course, and [I knew that already](https://stackoverflow.com/a/16056691/100297), but I've made it explicit in my answer now too. – Martijn Pieters Jan 15 '19 at 19:10
-2

The second recommendation would best suit your needs. The conditional statement if cond: will return true if the condition itself is true. It will not change the behavior of the code, as it is just shorthand for if cond == True:.