Истинные значения — это значения, которые оценивается как True при использовании в булевом контексте. Аналогично, ложные значения — это значения, которые оцениваются как False. Это полезная фича Python и некоторых других языков.
Применение этого функционала языка позволяет писать лаконичный и незагроможденный код. Однако с использованием истинных и ложных значений связаны некоторые ловушки, в которые могут угодить новички.
Хотите скачать книги по Python в 2 клика? Тогда вам в наш телеграм канал PythonBooks
Следующие значения являются ложными:
А это истинные значения:
Вот несколько примеров того, как истинные значения могут сделать ваш код немного понятнее. Рассмотрим следующий код:
n = 1 if n != 0: print("n is not zero")
Этот код можно сделать немного аккуратнее, применив истинные значения. Если значение n
не равно нулю, оно оценивается как True, поэтому предложение if
будет выполнено:
n = 1 if n: print("n is not zero")
Вот код, который выводит список только в том случае, если он не пуст:
k = [...] if len(k)!=0: print(k)
Этот код также можно упорядочить, используя истинность значений. Если список не пуст, он оценивается как True и будет напечатан:
k = [...] if k: print(k)
Наконец, этот код предложит пользователю ввести свое имя, но только если переменная name
пуста:
name = ... if len(name)==0: name = input("What is your name?")
Этот код делает то же самое, используя истинность значений:
name = ... name = name or input("What is your name?")
Чтобы понять, как это работает, обратите внимание, что в коде используется оператор or
с двумя значениями: name
и input(...)
.
Сначала код оценивает значение name
. Если name
истинно (т.е. строка не пустая), то выражение с or
будет истинным. Оператор or
оценивает операнды слева направо, и если первый — True, результат возвращается немедленно.
От редакции Pythonist: чуть подробнее о логических операторах читайте в статье «Операторы в Python».
Python не нужно вызывать функцию input()
, потому что он уже знает, что результат будет истинным. Более того, Python гарантированно не будет оценивать второе выражение, если первое истинно. Он просто сразу вернет имеющееся истинное значение, т.е. начальное значение переменной name
.
А если name
изначально является пустой строкой? В этом случае Python не знает, будет ли все выражение or
истинным или ложным. Чтобы определиться, ему придется оценить второй операнд. Это означает, что если переменная name
пуста, нужно вызвать функцию input()
. Пользователю будет предложено ввести свое имя, и все, что он введет, будет возвращено как истинное значение и присвоено переменной name
.
К истинности значений, возможно, придется немного привыкнуть, если вы не сталкивались с этой концепцией раньше, но в целом это хорошая вещь. Истинные значения делают ваш код менее загроможденным и часто позволяют избежать ненужных сравнений или вызовов len
.
От редакции Pythonist: читайте также «Функция len() в Pyhton».
Рассмотрим этот код:
import math def checked_sqrt(x): try: return math.sqrt(x) except ValueError: return None answer = checked_sqrt(4) if answer: print(answer) else: print("Error calculating square root")
Мы определяем функцию checked_sqrt
, которая вызывает функцию math.sqrt
для вычисления квадратного корня.
Однако функция math.sqrt
выдаст ошибку ValueError, если x
будет отрицательным, поскольку отрицательные числа не имеют квадратного корня. Наша функция обрабатывает этот случай, возвращая None, чтобы указать, что значение не может быть вычислено.
Затем мы вызываем checked_sqrt
и проверяем ответ. Если мы вызываем функцию со значением 4, функция вычислит квадратный корень как 2.0, что является истинным значением, поэтому оператор if
выведет ответ.
Если мы вызовем checked_sqrt
со значением -4, произойдет исключение, и функция вернет None. Это ложное значение, поэтому оператор if
выведет сообщение об ошибке.
Все это нормально. Но что если мы передадим значение 0? math.sqrt
вычислит квадратный корень, который, конечно же, равен 0.0. Поскольку исключения не было, checked_sqrt
вернет значение 0.0.
Но когда мы доходим до оператора if
, answer
имеет значение 0.0, которое оценивается как ложное (просто строка None). Это означает, что наш код неправильно выведет сообщение об ошибке, несмотря на то, что квадратный корень был вычислен правильно.
В чем здесь дело? Основная проблема заключается в том, что проверка истинности значения answer не может отличить случаи None и 0.0.
Если копнуть глубже, проблема заключается в том, что Python использует динамическую типизацию. Это значит, что функция checked_sqrt
может возвращать разные типы данных (float или объект None) при разных обстоятельствах. Если бы мы попробовали выполнить этот код на Java, например, checked_sqrt
была бы объявлена как возвращающая float, поэтому возврат null был бы невозможен (это привело бы к ошибке компиляции).
Это усугубляется неправильным направлением кода. Функция явно возвращает None, но тот факт, что она также может возвращать другое ложное значение 0.0, не очевиден. Беглый взгляд на код может убедить вас (ошибочно), что None — единственное возвращаемое значение, которое будет оценено как ложное.
Во-первых, стоит помнить, что во многих случаях это не будет проблемой. Допустим, у вас есть локальная переменная, которая объявлена со строковым значением. Если при использовании этой переменной в принципе не может случиться, что она будет содержать что-либо, кроме строкового значения, то совершенно нормально использовать ее истинность для проверки того, пуста ли она.
Это верно, если переменная принимает значение из функции, возвращающей только строковые значения. Так будет, если это значение является параметром этой функции, который должен быть только строковым. Для проверки пустоты строки значение должно быть строкой, поэтому допустимо использовать истинность его значения. Значение строки не должно быть None (это не предполагается), поэтому вы можете игнорировать такую возможность.
Если вас беспокоит, что значение может оказаться не строкой, то это отдельный вопрос. Нужный тип значения обеспечивается с помощью подсказок типов, проверки типов во время выполнения, модульных тестов, ревью кода и так далее. Но в основном теле вашей функции вы должны считать, что значение имеет тот тип, который должен быть.
Таким образом, эта проблема касается только ситуаций, когда значение законно может иметь разные типы. Приведенный выше пример, где переменная может иметь допустимое значение None, является очень типичной.
Одно из решений очень простое. В таких случаях не используйте проверку на истинность. Вместо этого явно проверяйте, является ли значение None:
if answer is not None: print(answer) else: print("Error calculating square root")
Решение простое, сложность заключается в том, чтобы приучить себя всегда помнить о проблемах такого рода.
Второе решение заключается в том, чтобы полностью избежать проблемы, не позволяя функции возвращать неоднозначный результат. Например:
(True, value)
будет означать, что value
валидно. (False, value)
укажет на отсутствие валидного результата, и value
должно быть проигнорировано.Опциональный объект — это специальный объект, содержащий значение, которое может присутствовать или отсутствовать. Он включает методы для проверки наличия значения, получения значения, если оно есть, и другие полезные функции. По сути, это более чистая альтернатива неформальному протоколу value-or-none.
Истинные значения — очень полезная функция Python, но в определенных обстоятельствах они могут создавать двусмысленность и ошибки. В этой статье было приведено несколько решений этой проблемы, которые можно применять в зависимости от конкретных обстоятельств.
Перевод статьи «The Hidden Traps of Python Truthy Values».
Pydantic - это мощная библиотека проверки данных и управления настройками для Python, созданная для повышения…
Python предлагает набор библиотек, удовлетворяющих различные потребности в визуализации, будь то академические исследования, бизнес-аналитика или…
В Python для представления данных в двоичной форме можно использовать байты. Из этой статьи вы…
В этой статье рассказывается о том, что такое Werkzeug и как Flask использует его для…
При работе с датами часто возникает необходимость прибавлять к дате или вычитать из нее различные…
В этом руководстве мы рассмотрим, как добавить социальную аутентификацию с помощью GitHub и Google в…