Обновление: пожалуйста, посмотрите мое обсуждение, если вы хотите углубиться в эту тему! Спасибо всем за ваши отзывы по этому поводу!
У меня есть логический (иш) флаг, который может быть 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")
Эта тема обсуждалась ранее:
Кажется, это самый близкий ответ на мой вопрос из доступных (с вариантом A выше), но даже этот ответ неоднозначен (предполагается также вариант C):
Однако в этом ответе указывается, что if not flag_bool эквивалентно if bool(flag_value) == False, что означает, что проверка эквивалентности False с использованием оператора == является официальным методом Python проверки False (вариант D):
Но это прямо противоречит ответу о том, что == False (Вариант D) никогда не следует использовать:
Часть PEP8, где говорится: «Не сравнивайте логические значения с True или False, используя ==:»? peps.python.org/pep-0008
@CharlesDuffy Я специально хочу проверить только случай False в этом вопросе. Уже приведено достаточное решение для проверки нескольких значений в блоке операторов if.
Возможно, вам понравится этот предыдущий мой ответ, в котором есть реализация чего-то похожего на класс.
@kindall в настоящее время метод получения атрибутов является универсальным, и лишь небольшая часть атрибутов является логическими. Я рассмотрю возможность явной инициализации логических атрибутов в class ... __init__, однако тогда мне нужно будет добавить еще один флаг, чтобы указать, следует ли читать логический флаг или нет.






Прежде всего, учитывая количество ловушек, которые такое представление данных таит в себе для неосторожных, возможно, стоит задуматься, хорошая ли это идея вообще.
Учитывая это, я думаю, можно с уверенностью предположить, что «запрет» на явные сравнения в 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:
Таким образом, 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?
@nocomment match ... case False: просто проверяет, равно ли значение bool_flag False. Я только что проверил, что это работает для False, None и True. match немного быстрее, чем if, но он не поддерживается в версии Python, которую я использую (хотя у меня есть среда для его тестирования).
Но как это проверить? Ты сказал «есть». Вы имели в виду is?
Комментарий, который я процитировал для варианта E, был match flag_bool: ... case False:, в операторе is нет case. Я не уверен, что вы спрашиваете? case is False выдает ошибку SyntaxError: invalid syntax.
Я говорю: посмотрите, что на самом деле делает тест match, чтобы выполнить свою работу. Как он определяет, соответствует ли значение False? Вы можете найти ответ в PEP и в байт-коде, и это обогатит ваш ответ.
Или, может быть, вы уже это сделали. Ваше «просто проверяет, равно ли значение bool_flagFalse» звучит так, как будто вы знаете, и означает, что оно проверяет идентичность, а не равенство или истинность. Я хочу сказать, что тот факт, что match под капотом выполняет проверку is, является еще одним доказательством того, что это правильно.
@nocomment нет, я не понял твой вопрос. Я посмотрел и процитировал PEP634. Спасибо! is False подтверждено «Pythonic».
Если вы действительно хотите это сделать:
if bool_flag is False:
pass
PEP8 является ориентиром. Могут быть проверяющие стиль, которые жалуются на это, и вам, возможно, придется засорить свой код #noqa, чтобы их успокоить, но в конце концов вам нужно решить, что лучше всего представляет то, что вы на самом деле пытаетесь сделать.
В этом случае, в частности, проверка значения на соответствие литералам True и False не рекомендуется PEP8 во многом потому, что существует ряд других условий, которые делают значение Truthy или False. При работе с параметрами или возвращаемыми значениями в/из других мест вашего кода или внешних библиотек может возникнуть ряд случаев, когда вы возвращаете действительно разные (а иногда и неожиданные) типы.
В вашем случае вы не ограничиваете свою переменную истинностью или ложью или даже истинностью или ложностью. Фактически, поскольку у вас есть возможность тринарного состояния, можно утверждать, что это вообще не логическое значение. При ближайшем приближении к логическому значению это необязательное [логическое значение]. Вместо этого вы могли бы утверждать, что это похоже на тип перечисления с тремя возможными значениями. С этой точки зрения вам нужно проверять фактическое значение, а не его правдивость. Последнее — это то, о чем говорит PEP8, когда не поощряет тестирование литералов.
Однако вот что следует помнить. Все аргументы в пользу отказа от тестирования логических литералов применимы и к вам. Помните ли вы через шесть месяцев, что значение нужно проверять на буквальное значение, а не просто проверять на истинность? Если кто-то другой просматривал ваш код или использовал возвращаемое значение из написанной вами функции, поймет ли он это?
Для удобства обслуживания вы можете полностью использовать другой тип. Возможно, тип перечисления. Это делает вещи более ясными, и вам будет намного проще объяснять, рассуждать и решать, если вы когда-нибудь захотите попробовать типизировать свой код.
Тринарное состояние — это артефакт, когда логические атрибуты инициализируются (до True или False) или не инициализируются (.get(attr) возвращает None). Возможно, мне следует отметить в вопросе, что bool_flag является атрибутом. Я мог бы сделать это свойством, но для этого потребовался бы дополнительный код, который я не готов реализовать, поскольку в настоящее время я рассчитываю на доступность и полноту __slots__.
Я понимаю. В моем коде есть много мест, в которых я также проверяю None или False, и по тем же причинам. Однако при этом нужно быть осторожным, и я признаю, что сталкивался с этой проблемой много раз. Усилия по исправлению на ранней стадии того стоят, если только это не эфемерный код или настолько глубоко внутри кодовой базы, что ни один пользователь никогда его не трогает. Я обнаружил, что при работе с неинициализированными переменными выдача исключений иногда может оказаться полезным подходом.
@AutumnKome, что ты имеешь в виду .get(attr)? Что такое .get? попытка получить доступ к атрибуту, который не был инициализирован, вызывает AttributeError. Похоже, вам, вероятно, не следует полагаться на то, что есть .get.
@AutumnKome, если вы на самом деле имели в виду getattr, он включает параметр, который вы можете использовать для определения значения по умолчанию вместо None.
Безопасные значения по умолчанию — это простой выход, если вам не нужно неинициализированное состояние. Я бы сказал, что это охватит большинство обычных случаев использования. Однако я знаю, что бывают случаи, когда неинициализированное состояние имеет свое собственное особое значение. Вам нужно решить, что лучше всего подходит вам и вашей кодовой базе.
@MarkRansom Я отредактировал, чтобы указать, что это атрибут класса dict. dict реализует getattribute как get.
@ChintalagiriShashank Я создал обсуждение и буду признателен за ваш вклад, если у вас есть время, спасибо! stackoverflow.com/beta/discussions/78469216/…
@AutumnKome Извините, я сейчас немного занят. Я оставил вкладку открытой. Я постараюсь изложить свои мысли через несколько дней, когда у меня будет время.
«Похоже, что эти двое строго запрещены PEP8» — Что заставляет вас так думать?