Strange PEP8 recommendation on comparing Boolean values to True or False
At the end of Python PEP 8 I'm reading:
Don't compare Boolean values to True or False using
==
Yes: if greeting: No: if greeting == True: Worse: if greeting is True:
I have no problem with that recommendation when the Boolean is True
, but it sounds strange when checking for False
.
If I want to know if a variable greeting is False, why shouldn't I write the following?
if greeting == False:
If I write if not greeting:
it will have a very different meaning that the above statement. What if greeting is None? What if it is an empty string? Does this PEP 8 recommendation means that variables storing Boolean values should only contains True or False and that None should be avoided for these 开发者_如何转开发variables?
To my eyes it looks like a recommendation coming from other languages with static typing and that does not fit well with Python, at least for comparing to False.
And by the way, why is if greeting is True:
described as worse than if greeting == True:
? Should we also understand that if greeting is False:
is also worse that if greeting == False:
?
I believe you're reading it wrong. Try not to think of greeting
as a noun so much as a verb ("I am greeting" instead of "This is a greeting").
You can see the clue in the preamble to PEP8:
One of Guido's key insights is that code is read much more often than it is written. The guidelines provided here are intended to improve the readability of code.
To that end, code should resemble the written or spoken word as much as possible. You don't say "If I am annoying you is true, let me know"
in real life, you just say "If I am annoying you, let me know"
.
That's one reason why you tend to see boolean variables like isOpen
and hasBeenProcessed
a lot since they aid in readability of the code.
You should never do something like:
if (isOpen == True)
or:
if (customerDead == False)
simply because you already have a boolean value in the variable name. All the equality is giving you is another boolean value and, invoking reduction ad absurdum, where would you stop?
if (isComplete == True) ...
if ((isComplete == True) == True) ...
if (((isComplete == True) == True) == True) ...
if ((((isComplete == True) == True) == True) == True)...
The simplest reason to not compare truth via ==
or !=
comparisons seems to be this:
0 is False # Result: False
0 == False # Result: True; 0 evaluates comparatively to False
1 is True # Result: False
1 == True # Result: True; 1 evaluates comparatively to True
is
checks whether the value passed is exactly True
/False
, not whether it evaluates to True
or False
.
This behavior allows this:
if var is False:
# False (bool) case
elif var is None:
# None case
elif var == 0:
# integer 0 case
whereas
if var == False:
# catches False & 0 case; but not None case, empty string case, etc.
which seems counter-intuitive -- which is why I expect PEP 8 says "don't do it".
As said here use is
for identity, but use ==
for equality.
You'd only want to use if var is True
when you need the bool value True
, but want to reject 1
, 'some string'
, etc.
Such cases are probably not obvious to most readers; I suspect PEP 8 claims it's "Worse" for being potentially misleading. From time to time it may be a necessary evil; but... if you find yourself needing is True
, it may be indicating a design issue. In any case, you should probably comment "why" you need exactly True
or False
if you do ever use is
.
This is part of duck typing. In Python, you usually don't want to restrict what you accept to a specific class, but to an object that exposes the proper API. For example, I can do this:
class MyProperty(object):
"""
A file-backed boolean property.
"""
def __init__(self, filename):
self.value = open(filename).read()
def __nonzero__(self):
return self.value != "0"
def save_to_disk(self):
# ... and so on
pass
def func(enabled):
if not enabled:
return
# ...
enable_feature = MyProperty("enable_feature")
func(enable_feature)
Saying if enabled == False
would cause this to not work.
False is a false value, but it's not the only false value. Avoid comparing to True and False for the same reason you avoid using isinstance
.
The way I understand it the PEP's recommendation implies that, if you know can be reasonably sure of the type of foo
(which is usually the case), then testing for the explicit false value is redundant and reduces readability. For example, in foo = [i for i in range(10) if i == x]
, you can be fairly sure that the only false value foo
can have is []
(assuming no exceptions are raised). In this case it is redundant to use foo == []
and not foo
is better.
On the other hand the semantic value of foo == []
or foo == False
is sometimes more valuable and should then be used (IMHO) in stead of not foo
. It depends on what, specifically, you are trying to communicate. In fact not foo
means "foo
has a false value?", whereas foo == False
means "foo
has the same value as False
?".
The PEP states that everything it contains are guidelines. There are exceptions to rules and this one is no different.
I’m not sure other comments answered your question. You say:
If I write
if not greeting:
it will have a very different meaning that the above statement. What if greeting is None? What if it is empty string?
Indeed, not greeting
and greeting == False
have different meaning. But the PEP 8 is not saying the opposite and it's not saying to not use greeting == False
. It says:
Don't compare boolean values to True or False using ==
Neither None
nor the empty string is a Boolean value. So use greeting == False
if appropriate and greeting
can be non-Boolean.
Someone commented below your questions:
... Why would it sometimes be None, sometimes be a bool, and sometimes be a str? That's just asking for all sorts of trouble.
That’s not. Here is a use case: You have a database of patients with a field saying whether the patient died from suicide or not. Let’s say we have class Patient
with attribute suicide
. suicide
would have three possible values:
True
meaning "yes, we know he committed suicide"False
meaning "no, we know he died from something else"None
meaning "we don't know actually".
Then, if you want to study patients not dying from suicide, you would do something like:
# load database
...
# Filter patients
database = [patient for patient in database if patient.suicide == False] # if greeting == False:
# Study database
...
QED. This is a typical case in data science. Value False
means you know something is false whereas value None
means you don't know anything.
I usually name my Boolean variables after the pattern IsName
, so in your case it is IsGreeting
. This makes the check read if IsGreeting
/if not IsGreeting
, which is very intuitive.
The ambiguities you are describing with if not
are the result of using non-Boolean types in Boolean comparisons. This should usually be avoided, as it is very confusing.
精彩评论