Проверка на False, когда переменная также может быть None или True

Обновление: пожалуйста, посмотрите мое обсуждение, если вы хотите углубиться в эту тему! Спасибо всем за ваши отзывы по этому поводу!


У меня есть логический (иш) флаг, который может быть True или False, с None в качестве дополнительного допустимого значения. Каждое значение имеет разное значение.

(Измените для пояснения: рассматриваемая переменная bool_flag является пользовательским атрибутом dictclass, который имеет значение None в неинициализированном состоянии, поскольку .get('bool_flag') возвращает None, и может быть установлен в True или False, хотя True и None обычно достаточно для значения проверяю свои нужды.)

Я понимаю, что Pythonic способ проверки True, None и not None:

if bool_flag: 
    print("This will print if bool_flag is True")
    # PEP8 approved method to check for True

if not bool_flag:
    print("This will print if bool_flag is False or None")
    # Also will print if bool_flag is an empty dict, sequence, or numeric 0

if bool_flag is None: 
    print("This will print if bool_flag  is None")

if bool_flag is not None: 
    print("This will print if bool_flag is True or False")
    # Also will print if bool_flag is initialized as anything except None

И если вам нужно проверить все три в блоке операторов if, вы должны использовать многоуровневый подход, например:

if bool_flag:
    print("This will print if bool_flag is True")
elif bool_flag is None:
    print("This will print if bool_flag  is None")
else:
    print("This will print if bool_flag is False")
    # Note this will also print in any case where flag_bool is neither True nor None

Но каков питонический способ простой проверки значения False (при проверке только False), когда флаг также может быть None или True в качестве допустимого значения? Я видел несколько вопросов, но, похоже, консенсуса нет.

Это «более питонично» писать:

# Option A:
if isinstance(bool_flag, bool) and not bool_flag:
    print("This will print if bool_flag is False")

# Option B:
if bool_flag is not None and not bool_flag:
    print("This will print if bool_flag is False")

## These two appear to be strictly prohibited by PEP8:
# Option C:
if bool_flag is False:
    print("This will print if bool_flag is False")

# Option D:
if bool_flag == False:
    print("This will print if bool_flag is False")

# Option E (per @CharlesDuffy):
match flag_bool:
    case False:
        print("This will print if bool_flag is False")

Эта тема обсуждалась ранее:

  1. Как правильно проверить ложность?
  2. Как в Python проверить, имеет ли переменная значение None, True или False
  3. Есть ли разница между «== False» и «is not» при проверке пустой строки?
  4. Почему сравнение строк с использованием символов «==» или «is» иногда дает другой результат?
  5. Есть ли разница между «==" и «есть»?

Кажется, это самый близкий ответ на мой вопрос из доступных (с вариантом A выше), но даже этот ответ неоднозначен (предполагается также вариант C):

  1. https://stackoverflow.com/a/37104262/22396214

Однако в этом ответе указывается, что if not flag_bool эквивалентно if bool(flag_value) == False, что означает, что проверка эквивалентности False с использованием оператора == является официальным методом Python проверки False (вариант D):

  1. https://stackoverflow.com/a/36936790/22396214

Но это прямо противоречит ответу о том, что == False (Вариант D) никогда не следует использовать:

  1. https://stackoverflow.com/a/2021257/22396214

«Похоже, что эти двое строго запрещены PEP8» — Что заставляет вас так думать?

no comment 09.05.2024 17:39

Часть PEP8, где говорится: «Не сравнивайте логические значения с True или False, используя ==:»? peps.python.org/pep-0008

AutumnKome 09.05.2024 17:43

@CharlesDuffy Я специально хочу проверить только случай False в этом вопросе. Уже приведено достаточное решение для проверки нескольких значений в блоке операторов if.

AutumnKome 09.05.2024 18:01

Возможно, вам понравится этот предыдущий мой ответ, в котором есть реализация чего-то похожего на класс.

kindall 09.05.2024 21:42

@kindall в настоящее время метод получения атрибутов является универсальным, и лишь небольшая часть атрибутов является логическими. Я рассмотрю возможность явной инициализации логических атрибутов в class ... __init__, однако тогда мне нужно будет добавить еще один флаг, чтобы указать, следует ли читать логический флаг или нет.

AutumnKome 09.05.2024 22:29
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
3
5
231
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Прежде всего, учитывая количество ловушек, которые такое представление данных таит в себе для неосторожных, возможно, стоит задуматься, хорошая ли это идея вообще.

Учитывая это, я думаю, можно с уверенностью предположить, что «запрет» на явные сравнения в PEP8 предназначен для того, чтобы отговорить новичков, которых действительно интересует только правдивость, писать такие вещи, как if (a != b) != False, которые они по какой-то причине всегда кажется, делает.

Если вы действительно хотите различать True, False и None, использование оператора is явно подойдет.

@SIGHUP в удаленном ответе предположил, что этот вопрос является артефактом формулировки PEP 8.

Вот разбивка проблемы, представленной PEP 8:

# PEP 8:
# Wrong:
if greeting is True:

# Cited by others (see link 8 after second suggestion) as also implying:
# Wrong:
if greeting is False:

Однако несколькими абзацами выше этого заявления PEP 8 гласит:

# PEP 8:
# Correct:
if foo is not None:

# This implies (widely accepted - see link 9 below):
# Correct:
if foo is None:
  1. В чем разница между «is None» и «== None»

Таким образом, Pythonic способ проверки эквивалентности False — это вариант C из второго предложения в ссылке 6:

# Option C:
if bool_flag is False:
    print("This will print if bool_flag is False")

Обновление: Согласно PEP 634, match ... case реализуется следующим образом:

Одноэлементные литералы None, True и False сравниваются с помощью оператора is.

Это подтверждает, что if...is False: является «Pythonic».

Вы также можете проверить, что делает match ... case False:. Как он решает, соответствует ли значение False?

no comment 09.05.2024 21:14

@nocomment match ... case False: просто проверяет, равно ли значение bool_flag False. Я только что проверил, что это работает для False, None и True. match немного быстрее, чем if, но он не поддерживается в версии Python, которую я использую (хотя у меня есть среда для его тестирования).

AutumnKome 09.05.2024 21:36

Но как это проверить? Ты сказал «есть». Вы имели в виду is?

no comment 09.05.2024 21:43

Комментарий, который я процитировал для варианта E, был match flag_bool: ... case False:, в операторе is нет case. Я не уверен, что вы спрашиваете? case is False выдает ошибку SyntaxError: invalid syntax.

AutumnKome 09.05.2024 21:48

Я говорю: посмотрите, что на самом деле делает тест match, чтобы выполнить свою работу. Как он определяет, соответствует ли значение False? Вы можете найти ответ в PEP и в байт-коде, и это обогатит ваш ответ.

no comment 09.05.2024 21:51

Или, может быть, вы уже это сделали. Ваше «просто проверяет, равно ли значение bool_flagFalse» звучит так, как будто вы знаете, и означает, что оно проверяет идентичность, а не равенство или истинность. Я хочу сказать, что тот факт, что match под капотом выполняет проверку is, является еще одним доказательством того, что это правильно.

no comment 09.05.2024 22:01

@nocomment нет, я не понял твой вопрос. Я посмотрел и процитировал PEP634. Спасибо! is False подтверждено «Pythonic».

AutumnKome 09.05.2024 22:50
Ответ принят как подходящий

Если вы действительно хотите это сделать:

if bool_flag is False:
   pass

PEP8 является ориентиром. Могут быть проверяющие стиль, которые жалуются на это, и вам, возможно, придется засорить свой код #noqa, чтобы их успокоить, но в конце концов вам нужно решить, что лучше всего представляет то, что вы на самом деле пытаетесь сделать.

В этом случае, в частности, проверка значения на соответствие литералам True и False не рекомендуется PEP8 во многом потому, что существует ряд других условий, которые делают значение Truthy или False. При работе с параметрами или возвращаемыми значениями в/из других мест вашего кода или внешних библиотек может возникнуть ряд случаев, когда вы возвращаете действительно разные (а иногда и неожиданные) типы.

В вашем случае вы не ограничиваете свою переменную истинностью или ложью или даже истинностью или ложностью. Фактически, поскольку у вас есть возможность тринарного состояния, можно утверждать, что это вообще не логическое значение. При ближайшем приближении к логическому значению это необязательное [логическое значение]. Вместо этого вы могли бы утверждать, что это похоже на тип перечисления с тремя возможными значениями. С этой точки зрения вам нужно проверять фактическое значение, а не его правдивость. Последнее — это то, о чем говорит PEP8, когда не поощряет тестирование литералов.

Однако вот что следует помнить. Все аргументы в пользу отказа от тестирования логических литералов применимы и к вам. Помните ли вы через шесть месяцев, что значение нужно проверять на буквальное значение, а не просто проверять на истинность? Если кто-то другой просматривал ваш код или использовал возвращаемое значение из написанной вами функции, поймет ли он это?

Для удобства обслуживания вы можете полностью использовать другой тип. Возможно, тип перечисления. Это делает вещи более ясными, и вам будет намного проще объяснять, рассуждать и решать, если вы когда-нибудь захотите попробовать типизировать свой код.

Тринарное состояние — это артефакт, когда логические атрибуты инициализируются (до True или False) или не инициализируются (.get(attr) возвращает None). Возможно, мне следует отметить в вопросе, что bool_flag является атрибутом. Я мог бы сделать это свойством, но для этого потребовался бы дополнительный код, который я не готов реализовать, поскольку в настоящее время я рассчитываю на доступность и полноту __slots__.

AutumnKome 09.05.2024 21:40

Я понимаю. В моем коде есть много мест, в которых я также проверяю None или False, и по тем же причинам. Однако при этом нужно быть осторожным, и я признаю, что сталкивался с этой проблемой много раз. Усилия по исправлению на ранней стадии того стоят, если только это не эфемерный код или настолько глубоко внутри кодовой базы, что ни один пользователь никогда его не трогает. Я обнаружил, что при работе с неинициализированными переменными выдача исключений иногда может оказаться полезным подходом.

Chintalagiri Shashank 09.05.2024 21:44

@AutumnKome, что ты имеешь в виду .get(attr)? Что такое .get? попытка получить доступ к атрибуту, который не был инициализирован, вызывает AttributeError. Похоже, вам, вероятно, не следует полагаться на то, что есть .get.

juanpa.arrivillaga 09.05.2024 21:45

@AutumnKome, если вы на самом деле имели в виду getattr, он включает параметр, который вы можете использовать для определения значения по умолчанию вместо None.

Mark Ransom 09.05.2024 21:46

Безопасные значения по умолчанию — это простой выход, если вам не нужно неинициализированное состояние. Я бы сказал, что это охватит большинство обычных случаев использования. Однако я знаю, что бывают случаи, когда неинициализированное состояние имеет свое собственное особое значение. Вам нужно решить, что лучше всего подходит вам и вашей кодовой базе.

Chintalagiri Shashank 09.05.2024 21:52

@MarkRansom Я отредактировал, чтобы указать, что это атрибут класса dict. dict реализует getattribute как get.

AutumnKome 09.05.2024 22:25

@ChintalagiriShashank Я создал обсуждение и буду признателен за ваш вклад, если у вас есть время, спасибо! stackoverflow.com/beta/discussions/78469216/…

AutumnKome 12.05.2024 22:27

@AutumnKome Извините, я сейчас немного занят. Я оставил вкладку открытой. Я постараюсь изложить свои мысли через несколько дней, когда у меня будет время.

Chintalagiri Shashank 14.05.2024 08:44

Другие вопросы по теме