Странная рекомендация PEP8 при сравнении логических значений с True или False
В конце python PEP8 Я читаю:
У меня нет проблем с этой рекомендацией, когда логическое значение True
, но это звучит странно при проверке False
Если я хочу знать, является ли переменное приветствие False, почему я не должен писать:
if greeting == False:
Если я напишу if not greeting:
, это будет иметь совсем другое значение, чем приведенное выше утверждение. Что, если приветствие - Нет? Что, если это пустая строка? Является ли эта рекомендация PEP8 означающей, что переменные, хранящие логические значения, должны содержать только True или False и что для этих переменных следует избегать None?
На мой взгляд, это похоже на рекомендацию, поступающую с других языков со статической типизацией и которая не подходит для python, по крайней мере, для сравнения с False.
И, кстати, кто-нибудь знает, почему if greeting is True:
описывается хуже, чем if greeting == True:
? Должны ли мы также понимать, что if greeting is False:
также хуже, чем if greeting == False:
?
Ответы
Ответ 1
Как я понимаю, рекомендация PEP подразумевает, что, если вы знаете, может быть разумно уверен в типе foo
(как правило, это так), тогда тестирование на явное ложное значение является избыточным и уменьшает читаемость. Например, в foo = [i for i in range(10) if i == x]
вы можете быть уверены, что единственное ложное значение foo
может иметь значение []
(при условии, что исключений не возникает). В этом случае избыточно использовать foo == []
и not foo
лучше.
С другой стороны, семантическое значение foo == []
или foo == False
иногда более ценно и затем должно использоваться (IMHO) вместо not foo
. Это зависит от того, что именно вы пытаетесь общаться. Фактически not foo
означает, что "foo
имеет a ложное значение?", Тогда как foo == False
означает, что "foo
имеет то же значение, что и False
?".
PEP утверждает, что все, что содержит, содержит рекомендации. Существуют исключения из правил, и это ничем не отличается.
Ответ 2
Я считаю, что вы читаете это неправильно. Попытайтесь не думать о greeting
как о существительном, как о глаголе ( "Я приветствую" вместо "Это приветствие" ).
Вы можете увидеть ключ в преамбуле к PEP8:
Один из ключевых соображений Guido заключается в том, что код читается гораздо чаще, чем он написан. Приведенные здесь рекомендации предназначены для улучшения читаемости кода.
С этой целью код должен напоминать написанное или произнесенное слово как можно больше. Вы не говорите "If I am annoying you is true, let me know"
в реальной жизни, вы просто говорите "If I am annoying you, let me know"
.
Это одна из причин, по которой вы часто видите логические переменные типа isOpen
и hasBeenProcessed
, поскольку они помогают в удобочитаемости кода.
Вы никогда не должны делать что-то вроде:
if (isOpen == True)
или
if (customerDead == False)
просто потому, что у вас уже есть логическое значение в имени переменной. Все равенство дает вам другое логическое значение и, вызывая сокращение ad absurdum, где бы вы остановились?
if (isComplete == True) ...
if ((isComplete == True) == True) ...
if (((isComplete == True) == True) == True) ...
if ((((isComplete == True) == True) == True) == True)...
Ответ 3
Это часть утиного набора текста. В Python вы обычно не хотите ограничивать то, что вы принимаете, к определенному классу, а к объекту, который предоставляет соответствующий API. Например, я могу это сделать:
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)
Высказывание if enabled == False
приведет к тому, что это не сработает.
False - ложное значение, но это не единственное ложное значение. Избегайте сравнения с True и False по той же причине, что вы избегаете использования isinstance
.
Ответ 4
Простейшая причина не сравнивать истину с помощью сравнений ==
или !=
выглядит следующим образом:
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
проверяет, является ли переданное значение точно True
/False
, а не является ли оно равным True
или False
.
Это позволяет:
if var is False:
# False (bool) case
elif var is None:
# None case
elif var == 0:
# integer 0 case
тогда
if var == False:
# catches False & 0 case; but not None case, empty string case, etc.
который кажется противоречивым - вот почему я ожидаю, что PEP8 говорит "не делай этого".
Как сказано в здесь, используйте is
для идентификации, но используйте ==
для равенства.
Вы должны использовать if var is True
, когда вам нужно значение bool True
, но хотите отклонить 1
, 'some string'
и т.д.
Такие случаи, вероятно, не очевидны для большинства читателей; Я подозреваю, что PEP8 утверждает, что "Хуже" за то, что он потенциально вводит в заблуждение. Время от времени это может быть необходимым злом; но... если вам нужно is True
, это может указывать на проблему с дизайном. В любом случае вам, вероятно, следует прокомментировать "почему" вам нужно точно True
или False
, если вы когда-либо использовали is
.
Ответ 5
Я не уверен, что другие комментарии ответили на ваш вопрос. Вы говорите:
Если я напишу if not greeting:
, это будет иметь совсем другое значение, чем приведенное выше утверждение. Что, если приветствие - Нет? Что если это пустая строка?
Действительно, not greeting
и greeting == False
имеют разное значение. Но PEP8 не говорит об обратном и не говорит, чтобы не использовать greeting == False
. Это говорит:
Не сравнивайте логические значения с True или False, используя ==
Ни None
, ни пустая строка не являются логическим значением. Поэтому используйте greeting == False
, если необходимо, и greeting
может быть не булевым.
Кто-то прокомментировал ваши вопросы ниже:
... Почему иногда это будет None, иногда будет bool, а иногда и str? Это просто напрашивается на всякие неприятности.
Это не. Вот пример использования: у вас есть база данных пациентов с полем, в котором указано, умер пациент от самоубийства или нет. Допустим, у нас есть класс Patient
с атрибутом suicide
. suicide
может иметь три возможных значения:
True
означает "да, мы знаем, что он покончил с собой"
False
означает "нет, мы знаем, что он умер от чего-то другого"
None
означает "мы на самом деле не знаем".
Затем, если вы хотите изучать пациентов, не умирающих от самоубийства, вы должны сделать что-то вроде:
# load database
...
# filter patients
database = [patient for patient in database if patient.suicide == False] # if greeting == False:
# study database
...
QED. Это типичный случай в науке о данных. Значение False
означает, что вы знаете, что что-то неверно, тогда как значение None
означает, что вы ничего не знаете.
Ответ 6
Я обычно называю свои логические переменные после шаблона IsName
, поэтому в вашем случае IsGreeting
. Это делает проверку прочитанной if IsGreeting
/if not IsGreeting
, что очень интуитивно.
Неопределенности, которые вы описываете с помощью if not
, являются результатом использования небулевых типов в булевых сравнениях. Обычно этого следует избегать, поскольку это очень запутывает.