Странная рекомендация PEP8 при сравнении логических значений с True или False

В конце python PEP8 Я читаю:

  • Не сравнивайте логические значения с True или False с помощью ==

    Yes:   if greeting:
    No:    if greeting == True:
    Worse: if greeting is True:
    

У меня нет проблем с этой рекомендацией, когда логическое значение 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, являются результатом использования небулевых типов в булевых сравнениях. Обычно этого следует избегать, поскольку это очень запутывает.