Логические значения Python: Используйте в своем коде истинные значения
Оглавление
- Логический тип Python
- Логические операторы
- Операторы сравнения
- Логическое тестирование Python
- Заключение
Тип Python Boolean - это один из встроенных типов данных Python. Он используется для представления истинностного значения выражения. Например, выражение 1 <= 2 равно True, в то время как выражение 0 == 1 равно False. Понимание того, как ведут себя логические значения Python, важно для успешного программирования на Python.
В этом руководстве вы узнаете, как:
- Манипулировать логическими значениями с помощью Логических операторов
- Преобразовать логические значения в другие типы
- Преобразовать другие типы в Логические значения Python
- Используйте логические значения Python для написания эффективного и удобочитаемого Кода на Python
Логический тип Python
Логический тип Python имеет только два возможных значения:
TrueFalse
Никакое другое значение не будет иметь bool в качестве своего типа. Вы можете проверить тип True и False с помощью встроенного type():
>>> type(False)
<class 'bool'>
>>> type(True)
<class 'bool'>
Значение type() как для False, так и для True равно bool.
Тип bool является встроенным в, что означает, что он всегда доступен в Python и не нуждается в импорте. Однако само по себе название не является ключевым словом в языке. Хотя приведенное ниже выражение считается некорректным, его можно присвоить названию bool:
>>> bool
<class 'bool'>
>>> bool = "this is not a type"
>>> bool
'this is not a type'
Хотя это технически возможно, во избежание путаницы настоятельно рекомендуется не присваивать другое значение bool.
Логические значения Python в качестве ключевых слов
Встроенные имена не являются ключевыми словами. Что касается языка Python, то это обычные переменные. Если вы присвоите им значение, то переопределите встроенное значение.
В отличие от этого, имена True и False являются , а не встроенными. Это ключевых слов. В отличие от многих других ключевых слов на Python, True и False являются выражениями на языке Python . Поскольку они являются выражениями, их можно использовать везде, где можно использовать другие выражения, такие как 1 + 1.
Переменным можно присвоить логическое значение, но невозможно присвоить значение True:
>>> a_true_alias = True
>>> a_true_alias
True
>>> True = 5
File "<stdin>", line 1
SyntaxError: cannot assign to True
Поскольку True является ключевым словом, вы не можете присвоить ему значение. То же правило применяется к False:
>>> False = 5
File "<stdin>", line 1
SyntaxError: cannot assign to False
Вы не можете присвоить False, потому что это ключевое слово в Python. Таким образом, True и False ведут себя как другие числовые константы. Например, вы можете передать 1.5 функциям или присвоить его переменным. Однако присвоить значение 1.5 невозможно. Оператор 1.5 = 5 недопустим в Python. Как 1.5 = 5, так и False = 5 являются недопустимым кодом на Python и при анализе вызовут SyntaxError.
Логические значения Python в виде чисел
Логические значения считаются числовым типом в Python. Это означает, что они являются числами по сути. Другими словами, вы можете применять арифметические операции к логическим значениям, а также сравнивать их с числами:
>>> True == 1
True
>>> False == 0
True
>>> True + (False / True)
1.0
Существует не так много способов использования числовой природы логических значений, но есть один метод, который может вам пригодиться. Поскольку True равно 1, а False равно 0, сложение логических значений вместе - это быстрый способ подсчитать количество значений True. Это может пригодиться, когда вам нужно подсчитать количество элементов, удовлетворяющих определенному условию.
Например, если вы хотите проанализировать стих из классического детского стихотворения, чтобы увидеть, в какой части строк содержится слово "the", то тот факт, что True равно 1, а False равно 0 может оказаться весьма полезным:
>>> lines="""\
... He took his vorpal sword in hand;
... Long time the manxome foe he sought—
... So rested he by the Tumtum tree
... And stood awhile in thought.
... """.splitlines()
>>> sum("the" in line.lower() for line in lines) / len(lines)
0.5
Суммирование всех значений в генераторном выражении, подобном этому, позволяет узнать, сколько раз True появляется в генераторе. Количество раз, когда True используется в генераторе, равно количеству строк, содержащих слово "the", без учета регистра. Разделив это число на общее количество строк, вы получите отношение совпадающих строк к общему количеству строк.
Чтобы понять, почему это работает, вы можете разбить приведенный выше код на более мелкие части:
>>> lines = """\
... He took his vorpal sword in hand;
... Long time the manxome foe he sought—
... So rested he by the Tumtum tree
... And stood awhile in thought.
... """
>>> line_list = lines.splitlines()
>>> "the" in line_list[0]
False
>>> "the" in line_list[1]
True
>>> 0 + False + True # Equivalent to 0 + 0 + 1
1
>>> ["the" in line for line in line_list]
[False, True, True, False]
>>> False + True + True + False
2
>>> len(line_list)
4
>>> 2/4
0.5
Переменная line_list содержит список строк. В первой строке нет слова "the", поэтому "the" in line_list[0] равно False. Во второй строке действительно появляется "the", поэтому "the" in line_list[1] равно True. Поскольку логические значения - это числа, вы можете добавить их к числам, и 0 + False + True даст 1.
Поскольку ["the" in line for line in line_list] - это список из четырех логических значений, вы можете сложить их вместе. Когда вы добавляете False + True + True + False, вы получаете 2. Теперь, если вы разделите этот результат на 4, то есть на длину списка, вы получите 0.5. Слово "the" появляется в половине выделенных строк. Это полезный способ воспользоваться тем фактом, что логические значения - это числа.
Логические операторы
Логические операторы - это те, которые принимают Логические входные данные и возвращают Логические результаты.
Примечание: Позже вы увидите, что эти операторы могут иметь другие входные данные и не всегда возвращают логические результаты. На данный момент во всех примерах будут использоваться логические входные данные и результаты. Вы увидите, как это обобщается на другие значения в разделе, посвященном правдивости.
Поскольку логические значения Python имеют только два возможных варианта, True или False, можно полностью указать операторы в терминах результатов, которые они присваивают каждой возможной комбинации входных данных. Эти спецификации называются таблицами истинности, поскольку они отображаются в виде таблицы.
Как вы увидите позже, в некоторых ситуациях для определения значения оператора достаточно знать только один вход. В таких случаях другие входные данные не обрабатываются. Это называется оценкой короткого замыкания.
Важность оценки короткого замыкания зависит от конкретного случая. В некоторых случаях это может незначительно повлиять на вашу программу. В других случаях, например, когда вычисление выражений, не влияющих на результат, требует больших вычислительных затрат, это обеспечивает значительное повышение производительности. В самых экстремальных случаях корректность вашего кода может зависеть от вычисления короткого замыкания.
Операторы без входных данных
Вы можете рассматривать True и False как логические операторы, которые не принимают никаких входных данных. Один из этих операторов всегда возвращает True, а другой всегда возвращает False.
Иногда полезно рассматривать логические значения Python как операторы. Например, такой подход помогает напомнить вам, что они не являются переменными. По той же причине, по которой вы не можете назначить значение +, невозможно назначить значение True или False.
В Python существует только два логических значения. Логический оператор без входных данных всегда возвращает одно и то же значение. Из-за этого True и False являются единственными логическими операторами, которые не принимают входные данные.
Логический оператор not
Единственным логическим оператором с одним аргументом является not. Он принимает один аргумент и возвращает противоположный результат: False для True и True для False. Вот таблица истинности:
A |
not A |
|---|---|
True |
False |
False |
True |
В этой таблице показано, что not возвращает значение, противоположное истинности аргумента. Поскольку not принимает только один аргумент, это не приводит к короткому замыканию. Он вычисляет свой аргумент, прежде чем вернуть свой результат:
>>> not True
False
>>> not False
True
>>> def print_and_true():
... print("I got called")
... return True
...
>>> not print_and_true()
I got called
False
В последней строке показано, что not оценивает свои входные данные перед возвратом False.
Возможно, вам интересно, почему нет других логических операторов, которые принимали бы один аргумент. Чтобы понять, почему, вы можете посмотреть на таблицу, в которой показаны все теоретически возможные логические операторы, которые принимали бы один аргумент:
A |
not A |
Identity | Yes | No |
|---|---|---|---|---|
True |
False |
True |
True |
False |
False |
True |
False |
True |
False |
Существует только четыре возможных оператора с одним аргументом. Кроме not, все остальные три оператора имеют несколько причудливые названия, поскольку на самом деле их не существует:
-
Identity: Поскольку этот оператор просто возвращает свои входные данные, вы можете просто удалить его из своего кода без какого-либо эффекта. -
Yes: Это оператор короткого замыкания, поскольку он не зависит от своего аргумента. Вы могли бы просто заменить его наTrueи получить тот же результат. -
No: Это еще один оператор короткого замыкания, поскольку он не зависит от своего аргумента. Вы могли бы просто заменить его наFalseи получить тот же результат.
Ни один из других возможных операторов с одним аргументом не был бы полезен.
Логический оператор and
Оператор and принимает два аргумента. Его значение равно False, если только оба входных параметра не равны True. Вы могли бы определить поведение and с помощью следующей таблицы истинности:
A |
B |
A and B |
|---|---|---|
True |
True |
True |
False |
True |
False |
True |
False |
False |
False |
False |
False |
Эта таблица является подробной. Однако она иллюстрирует то же поведение, что и в описании выше. Если A равно False, то значение B не имеет значения. Из-за этого and происходит короткое замыкание, если первый вход равен False. Другими словами, если первый вход равен False, то второй вход не вычисляется.
Следующий код содержит второй входной сигнал, который имеет побочный эффект , выводимый на печать, чтобы привести конкретный пример:
>>> def print_and_return(x):
... print(f"I am returning {x}")
... return x
...
>>> True and print_and_return(True)
I am returning True
True
>>> True and print_and_return(False)
I am returning False
False
>>> False and print_and_return(True)
False
>>> False and print_and_return(False)
False
В последних двух случаях ничего не выводится. Функция не вызывается, поскольку при ее вызове нет необходимости определять значение оператора and. Важно помнить о коротких замыканиях, когда выражения имеют побочный эффект. В последних двух примерах оценка короткого замыкания предотвращает возникновение побочных эффектов при печати.
Одним из примеров, в котором такое поведение может иметь решающее значение, является код, который может вызывать исключения:
>>> def inverse_and_true(n):
... 1 // n
... return True
...
>>> inverse_and_true(5)
True
>>> inverse_and_true(0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in inverse_and_true
ZeroDivisionError: integer division or modulo by zero
>>> False and inverse_and_true(0)
False
Функция inverse_and_true(), по общему признанию, глупа, и многие средства компоновки предупреждали бы о бесполезности выражения 1 // n. Это действительно служит для того, чтобы аккуратно завершать работу, если в качестве параметра задано 0, поскольку деление на 0 недопустимо. Однако последняя строка не вызывает исключения. Из-за вычисления короткого замыкания функция не вызывается, деление на 0 не выполняется, и исключение не генерируется.
В отличие от этого, True and inverse_and_true(0) вызовет исключение. В этом случае для получения результата and потребуется значение второго входного параметра. Как только будет вычислен второй входной сигнал, будет вызван inverse_and_true(0), он разделится на 0, и возникнет исключение.
Логический оператор or
Значение оператора or равно True, если только оба его входных сигнала не равны False. Оператор or также может быть определен с помощью следующей таблицы истинности:
A |
B |
A or B |
|---|---|---|
True |
True |
True |
False |
True |
True |
True |
False |
True |
False |
False |
False |
Эта таблица является подробной, но она имеет то же значение, что и приведенное выше объяснение.
При неофициальном употреблении слово или может иметь одно из двух значений:
-
Исключительный или - это то, как или используется во фразе “Вы можете подать заявку на продление или сдать домашнее задание вовремя”. В этом случае вы не сможете одновременно подать заявку на продление и сдать домашнее задание вовремя.
-
Символ , включающий в себя или, иногда обозначается с помощью союза и/или. Например, “Если вы хорошо справитесь с этим заданием, то сможете получить прибавку к зарплате и/или продвижение по службе” означает, что вы можете получить и прибавку, и продвижение по службе.
Когда Python интерпретирует ключевое слово or, он делает это, используя включающий или . Если оба входных сигнала равны True, то результатом or будет True.
Поскольку он использует включающий или , оператор or в Python также использует вычисление короткого замыкания. Если первый аргумент равен True, то результатом будет True, и нет необходимости оценивать второй аргумент. Следующие примеры демонстрируют оценку короткого замыкания в or:
>>> def print_and_true():
... print("print_and_true called")
... return True
...
>>> True or print_and_true()
True
>>> False or print_and_true()
print_and_true called
True
Второй вход не вычисляется с помощью or, если только первый не равен False. На практике вычисление короткого замыкания по формуле or используется гораздо реже, чем по формуле and. Однако важно помнить об этом при чтении кода.
Другие логические операторы
Математическая теория булевой логики определяет, что никакие другие операторы, кроме not, and, и or, не требуются. Все остальные операторы для двух входов могут быть заданы в терминах этих трех операторов. Все операторы для трех или более входов могут быть заданы в терминах операторов для двух входов.
На самом деле, даже наличие как or, так и and является избыточным. Оператор and может быть определен в терминах not и or, а оператор or может быть определен в терминах not и and. Однако and и or настолько полезны, что во всех языках программирования есть и то, и другое.
Существует шестнадцать возможных логических операторов с двумя входами. За исключением and и or, они редко требуются на практике. Из-за этого, True, False, not, and, и or являются единственными встроенными логическими операторами Python.
Операторы сравнения
Некоторые из операторов Python проверяют, существует ли связь между двумя объектами. Поскольку связь либо сохраняется, либо не сохраняется, эти операторы, называемые операторами сравнения, всегда возвращают логические значения.
Операторы сравнения являются наиболее распространенным источником логических значений.
Равенство и неравенство
Наиболее распространенными операторами сравнения являются оператор равенства (==) и оператор неравенства (!=). Практически невозможно написать сколько-нибудь значимый объем кода на Python, не используя хотя бы один из этих операторов.
Оператор равенства (==) - один из наиболее часто используемых операторов в коде Python. Вам часто приходится сравнивать либо неизвестный результат с известным, либо два неизвестных результата друг с другом. Некоторые функции возвращают значения, которые необходимо сравнить с сигналом, чтобы увидеть, обнаружено ли какое-либо граничное условие. Иногда вам нужно сравнить результаты двух функций друг с другом.
Оператор равенства часто используется для сравнения чисел:
>>> 1 == 1
True
>>> 1 == 1.0
True
>>> 1 == 2
False
Возможно, вы уже использовали операторы равенства. Это одни из наиболее распространенных операторов в Python. Для всех встроенных объектов Python и для большинства сторонних классов они возвращают логическое значение : True или False.
Примечание: Язык Python не требует, чтобы == и != возвращали логические значения. Такие библиотеки, как NumPy и pandas возвращают другие значения.
Вторым по популярности после оператора равенства является оператор неравенства (!=). Он возвращает True, если аргументы не равны, и False, если они равны. Примеры также разнообразны. Многие модульные тесты проверяют, что значение не равно определенному недопустимому значению. Веб-клиент может проверить, что код ошибки не соответствует 404 Not Found, прежде чем попробовать альтернативный вариант.
Вот два примера использования оператора неравенства Python:
>>> 1 != 2
True
>>> 1 != (1 + 0.0)
False
Пожалуй, самое удивительное в операторе неравенства Python - это то, что он вообще существует. В конце концов, вы могли бы достичь того же результата, что и 1 != 2, с помощью not (1 == 2). Python обычно избегает дополнительного синтаксиса и особенно дополнительных основных операторов, поскольку это легко достижимо другими средствами.
Однако неравенство используется так часто, что было сочтено целесообразным создать для него специальный оператор. В старых версиях Python, в серии 1.x, на самом деле было два разных синтаксиса.
В качестве первоапрельской шутки, Python по-прежнему поддерживает альтернативный синтаксис для неравенства с правильным __future__ импортом:
>>> from __future__ import barry_as_FLUFL
>>> 1 <> 2
True
Это никогда не должно использоваться в любом коде, предназначенном для реального использования. Однако это может пригодиться на вашем следующем вечере викторин по Python.
Сравнение заказов
Другим набором тестовых операторов являются операторы сравнения порядка . Существует четыре оператора сравнения порядка, которые можно классифицировать по двум признакам:
- Направление: Это меньше или больше, чем?
- Строгость: Допускается ли равенство или нет?
Поскольку эти два варианта являются независимыми, вы получаете 2 * 2 == 4 операторы сравнения порядка. Все четыре перечислены в этой таблице:
| Less than | Greater than | |
|---|---|---|
| Strict | < |
> |
| Not strict | <= |
>= |
Есть два варианта направления и два варианта строгости. В результате получается в общей сложности четыре оператора сравнения порядков.
Операторы сравнения порядка определены не для всех объектов. Некоторые объекты не имеют значимого порядка. Несмотря на то, что списки и кортежи упорядочены лексикографически, в словарях отсутствует осмысленный порядок:
>>> {1: 3} < {2: 4}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'dict' and 'dict'
Неочевидно, как следует упорядочивать словари. Согласно Дзен Python, перед лицом двусмысленности Python отказывается угадывать.
Хотя строки и целые числа упорядочиваются отдельно, межтиповые сравнения не поддерживаются:
>>> 1 <= "1"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '<=' not supported between instances of 'int' and 'str'
Опять же, поскольку нет очевидного способа определить порядок, Python отказывается сравнивать их. Это аналогично оператору сложения (+). Хотя вы можете добавлять строки к строкам и целые числа к целым числам, при добавлении строк к целым числам возникает исключение.
Когда операторы сравнения порядка определены как, в общем случае они возвращают логическое значение.
Примечание: Python не требует, чтобы операторы сравнения возвращали логические значения. Хотя все встроенные объекты Python и большинство сторонних объектов возвращают логические значения при сравнении, существуют исключения.
Например, операторы сравнения между массивами NumPy или фреймами данных pandas возвращают массивы и фреймы данных. Вы узнаете больше о взаимодействии NumPy и логических значений позже в этом руководстве.
Сравнение чисел в Python - распространенный способ проверки на соответствие граничным условиям. Обратите внимание, что < не допускает равенства, в то время как <= допускает:
>>> 1 <= 1
True
>>> 1 < 1
False
>>> 2 > 3
False
>>> 2 >= 2
True
Программисты часто используют операторы сравнения, не осознавая, что они возвращают логическое значение Python.
Оператор is
Оператор is проверяет идентичность объекта. Другими словами, x is y вычисляется как True только тогда, когда x и y вычисляются для одного и того же объекта. У оператора is есть противоположность - оператор is not.
Обычно is и is not используются для сравнения списков на предмет идентичности:
>>> x = []
>>> y = []
>>> x is x
True
>>> x is not x
False
>>> x is y
False
>>> x is not y
True
Даже если x == y, это не один и тот же объект. Оператор is not всегда возвращает значение, противоположное is. Нет никакой разницы между выражением x is not y и выражением not (x is y), за исключением удобочитаемости.
Имейте в виду, что в приведенных выше примерах показан оператор is, используемый только со списками. Поведение оператора is для неизменяемых объектов, таких как числа и строки, более сложное.
Оператор in
Оператор in проверяет наличие членства. Объект может определять, что он считает членами. Большинство последовательностей, таких как списки, рассматривают свои элементы как элементы-члены:
>>> small_even = [2, 4]
>>> 1 in small_even
False
>>> 2 in small_even
True
>>> 10 in small_even
False
Поскольку 2 является элементом списка, 2 in small_even возвращает True. Поскольку 1 и 10 отсутствуют в списке, другие выражения возвращают False. Во всех случаях оператор in возвращает логическое значение.
Поскольку строки представляют собой последовательности символов, вы можете ожидать, что они также будут проверять принадлежность. Другими словами, символы, которые являются членами строки, вернут True вместо in, в то время как те, которые этого не делают, вернут False:
>>> "e" in "hello beautiful world"
True
>>> "x" in "hello beautiful world"
False
Поскольку "e" является вторым элементом строки, первый пример возвращает True. Поскольку x не отображается в строке, второй пример возвращает False. Однако, наряду с отдельными символами, подстроки также считаются элементами строки:
>>> "beautiful" in "hello beautiful world"
True
>>> "belle" in "hello beautiful world"
False
Поскольку "beautiful" является подстрокой, оператор in возвращает True. Поскольку "belle" не является подстрокой, оператор in возвращает False. И это несмотря на то, что каждая отдельная буква в "belle" является частью строки.
Подобно операторам is и ==, оператор in также имеет противоположную функцию, not in. Вы можете использовать not in для подтверждения того, что элемент не является членом объекта.
Объединение операторов сравнения в цепочку
Операторы сравнения могут образовывать цепочки . Вы можете создавать цепочки операторов сравнения, разделяя выражения операторами сравнения для формирования более крупного выражения:
>>> 1 < 2 < 3
True
Выражение 1 < 2 < 3 представляет собой цепочку операторов сравнения. В ней есть выражения, разделенные операторами сравнения. Результатом будет True, поскольку обе части цепочки равны True. Вы можете разорвать цепочку, чтобы увидеть, как это работает:
>>> 1 < 2 and 2 < 3
True
, поскольку 1 < 2 возвращает True, а 2 < 3 возвращает True, and возвращает True. Цепочка сравнения эквивалентна использованию and для всех ее звеньев. В этом случае, поскольку True and True возвращает True, результатом всей цепочки будет True. Это означает, что если какое-либо из звеньев равно False, то вся цепочка равна False:
>>> 1 < 3 < 2
False
Эта цепочка сравнения возвращает False, поскольку не все ее звенья равны True. Поскольку цепочки сравнения представляют собой неявный оператор and, если хотя бы одно звено имеет значение False, то вся цепочка будет иметь значение False. Вы можете разбить цепочку, чтобы увидеть, как это работает:
>>> 1 < 3 and 3 < 2
False
В этом случае части цепочки принимают следующие логические значения:
1 < 3являетсяTrue3 < 2являетсяFalse
Это означает, что один из результатов равен True, а другой - False. Поскольку True and False равно False, значение всей цепочки равно False.
Вы можете смешивать типы и операции в цепочке сравнения до тех пор, пока типы можно сравнивать:
>>> 1 < 2 < 1
False
>>> 1 == 1.0 < 0.5
False
>>> 1 == 1.0 == True
True
>>> 1 < 3 > 2
True
>>> 1 < 2 < 3 < 4 < 5
True
Необязательно, чтобы все операторы были одинаковыми. Даже типы не обязательно должны быть одинаковыми. В приведенных выше примерах у вас есть три числовых типа:
intfloatbool
Это три разных числовых типа, но вы можете сравнивать объекты разных числовых типов без проблем.
Оценка цепи короткого замыкания
Если в цепочках используется неявный and, то в цепочках также должно произойти короткое замыкание. Это важно, потому что даже в тех случаях, когда сравнение заказов не определено, цепочка может возвращать False:
>>> 2 < "2"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'int' and 'str'
>>> 3 < 2 < "2"
False
Несмотря на то, что Python не может упорядочить сравнение целых чисел и строк, 3 < 2 < "2" вычисляется как False, поскольку он не вычисляет второе сравнение. В этом случае оценка короткого замыкания предотвращает другой побочный эффект: возникновение исключения.
Оценка короткого замыкания в цепочках сравнения может предотвратить другие исключения:
>>> 3 < 2 < (1//0)
False
Деление 1 на 0 привело бы к появлению ZeroDivisionError. Однако из-за вычисления с коротким замыканием Python не вычисляет недопустимое деление. Это означает, что Python пропускает оценку не только сравнения, но и входных данных для сравнения.
Еще один аспект, который важно понимать в цепочках сравнения, заключается в том, что когда Python выполняет вычисление элемента в цепочке, он вычисляет его только один раз:
>>> def foo():
... print("I'm foo")
... return 1
...
>>> 0 < foo() < 2
I'm foo
True
>>> (0 < foo()) and (foo() < 2)
I'm foo
I'm foo
True
Поскольку средние элементы вычисляются только один раз, не всегда безопасно преобразовывать x < y < z в (x < y) and (y < z). Хотя при оценке короткого замыкания цепь ведет себя как and, она оценивает все значения, включая промежуточные, только один раз.
Цепочки
особенно полезны для проверки диапазона , которые подтверждают, что значение попадает в заданный диапазон. Например, в ежедневном счете-фактуре, который включает количество отработанных часов, вы можете сделать следующее:
>>> hours_worked = 5
>>> 1 <= hours_worked <= 25
True
Если отработано 0 часов, то нет смысла отправлять счет-фактуру. С учетом перехода на летнее время максимальное количество часов в сутках составляет 25. Приведенная выше проверка диапазона подтверждает, что количество отработанных часов в день находится в пределах допустимого диапазона.
Операторы смешивания и объединения в цепочки
До сих пор все наши примеры включали в себя ==, !=, и сравнение по порядку. Однако вы можете объединить в цепочку все операторы сравнения в Python. Это может привести к неожиданному поведению:
>>> a = 0
>>> a is a < 1
True
>>> (a is a) < 1
False
>>> a is (a < 1)
False
Поскольку a is a < 1 является цепочкой сравнения, она вычисляется как True. Вы можете разбить цепочку на части:
- Выражение
a is aравноTrue, как и для любого значения, вычисленного относительно самого себя. - Выражение
a < 1равноTrue, поскольку0меньше, чем1.
Поскольку обе части равны True, цепочка вычисляется следующим образом True.
Однако люди, которые привыкли к другим операторам в Python, могут предположить, что, как и в других выражениях, содержащих несколько операторов, таких как 1 + 2 * 3, Python вставляет круглые скобки в выражение. Однако ни один из способов вставки круглых скобок не будет иметь значения True.
Вы можете понять, почему оба значения равны False, если разбить выражения на части. Если разбить первое выражение, то получится следующее:
>>> a = 0
>>> a is a
True
>>> True == 1
True
>>> (a is a) < 1
False
Вы можете видеть выше, что a is a возвращает True, как и для любого другого значения. Это означает, что (a is a) < 1 совпадает с True < 1. Логические значения - это числовые типы, и True равно 1. Таким образом, True < 1 совпадает с 1 < 1. Поскольку это строгое неравенство и 1 == 1, оно возвращает значение False.
Второе выражение работает по-другому:
>>> a = 0
False
>>> a < 1
True
>>> 0 is True
<stdin>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
False
Так как 0 меньше, чем 1, a < 1 возвращает True. Поскольку 0 != True, то не может быть так, что 0 is True.
Примечание: Не относитесь к сказанному выше SyntaxWarning легкомысленно. Использование is в числах может привести к путанице. Однако, в частности, для случаев, когда вы знаете, что числа не равны, вы можете знать, что is также вернет False. Хотя этот пример верен, он не является примером хорошего стиля программирования на Python.
Самый важный урок, который можно извлечь из этого, заключается в том, что связывать сравнения с is обычно не очень хорошая идея. Это сбивает читателя с толку и, вероятно, не является необходимым.
Как и is, оператор in и его противоположность, not in, часто могут давать неожиданные результаты при объединении в цепочку:
>>> "b" in "aba" in "cabad" < "cabae"
True
Чтобы максимально запутать ситуацию, в этом примере используются цепочки сравнений с различными операторами и in со строками для проверки наличия подстрок. Опять же, это не пример хорошо написанного кода! Однако важно уметь читать этот пример и понимать, почему он возвращает True.
Наконец, вы можете связать is not с not in:
>>> greeting = "hello"
>>> quality = "good"
>>> end_greeting = "farewell"
>>> greeting is not quality not in end_greeting
True
Обратите внимание, что порядок следования not в этих двух операторах различен! Отрицательными операторами являются is not и not in. Это соответствует обычному использованию в английском языке, но при изменении кода легко допустить ошибку.
Логическое тестирование на Python
Логическое значение Python чаще всего используется в операторе if. Эта инструкция будет выполнена, если значение равно True:
>>> 1 == 1
True
>>> if 1 == 1:
... print("yep")
...
yep
>>> 1 == 2
False
>>> if 1 == 2:
... print("yep")
...
print() вызывается только тогда, когда значение выражения равно True. Однако в Python вы можете присвоить любое значение if. Значения, которые if учитывают True, называются истинными,, а значения, которые if учитывают False, называются ложными.
if определяет, какие значения являются истинными, а какие ложными, путем внутреннего вызова встроенного bool(). Вы уже сталкивались с bool() как с логическим типом Python. При вызове он преобразует объекты в логические значения.
None как логическое значение
Одноэлементный объект None всегда является ложным:
>>> bool(None)
False
Это часто полезно в операторах if, которые проверяют наличие контрольного значения. Однако обычно лучше явно проверять идентичность с помощью is None. Иногда None может быть полезно в сочетании с оценкой короткого замыкания, чтобы установить значение по умолчанию.
Например, вы можете использовать or, чтобы заменить None пустым списком:
>>> def add_num_and_len(num, things=None):
... return num + len(things or [])
...
>>> add_num_and_len(5, [1, 2, 3])
8
>>> add_num_and_len(6)
6
В этом примере список не будет создан, если things является непустым списком, поскольку or приведет к короткому замыканию перед выполнением оценки [].
Числа как логические значения
Для чисел bool(x) эквивалентно x != 0. Это означает, что единственным ложным целым числом является 0:
>>> bool(3), bool(-5), bool(0)
(True, True, False)
Все ненулевые целые числа верны. Это также верно для чисел с плавающей запятой, включая специальные числа с плавающей запятой, такие как бесконечность и Не число (NaN).:
>>> import math
>>> [bool(x) for x in [0, 1.2, 0.5, math.inf, math.nan]]
[False, True, True, True, True]
Поскольку infinity и NaN не равны 0, они верны.
Сравнение равенств и неравенств для чисел с плавающей запятой - это тонкая операция. Поскольку выполнение bool(x) эквивалентно x != 0, это может привести к неожиданным результатам для чисел с плавающей запятой:
>>> bool(0.1 + 0.2 + (-0.2) + (-0.1))
True
>>> 0.1 + 0.2 + (-0.2) + (-0.1)
2.7755575615628914e-17
Вычисления с числами с плавающей запятой могут быть неточными. Из-за этого результаты bool() для чисел с плавающей запятой могут быть неожиданными.
В стандартной библиотеке Python больше числовых типов, и они подчиняются тем же правилам. Для не встроенных числовых типов bool(x) также эквивалентно x != 0. Модуль fractions находится в стандартной библиотеке. Как и другие числовые типы, единственной ложной дробью является 0/1:
>>> import fractions
>>> bool(fractions.Fraction("1/2")), bool(fractions.Fraction("0/1"))
(True, False)
Как и в случае с целыми числами и числами с плавающей запятой, дроби являются ложными только в том случае, если они равны 0.
Модуль decimal также находится в стандартной библиотеке. Десятичные дроби также являются ложными, только если они равны 0:
>>> import decimal, math
>>> with decimal.localcontext(decimal.Context(prec=3)) as ctx:
... bool(ctx.create_decimal(math.pi) - ctx.create_decimal(22)/7)
...
False
>>> with decimal.localcontext(decimal.Context(prec=4)) as ctx:
... bool(ctx.create_decimal(math.pi) - ctx.create_decimal(22)/7)
...
True
Число 22 / 7 является приближением числа Пи к двум знакам после запятой. Этот факт обсуждался Архимедом в III веке до н.э.. Когда разница между 22 / 7 и Pi вычисляется с такой точностью, результат является ложным. Когда разница вычисляется с более высокой точностью, разница не равна 0, и поэтому является достоверной.
Последовательности в виде логических значений
Как правило, объекты, имеющие len(), будут ложными, если результатом len() будет 0. Не имеет значения, являются ли они списками, кортежами, наборами, строками или байтовыми строками:
>>> bool([1]), bool([])
(True, False)
>>> bool((1,2)), bool(())
(True, False)
>>> bool({1,2,3}), bool(set())
(True, False)
>>> bool({1: 2}), bool({})
(True, False)
>>> bool("hello"), bool("")
(True, False)
>>> bool(b"xyz"), bool(b"")
(True, False)
Все встроенные объекты Python, имеющие длину, следуют этому правилу. Позже вы увидите некоторые исключения из этого правила для не встроенных объектов.
Другие типы в виде логических значений
Если типы не имеют len() или специально не определяют, являются ли они достоверными или ложными, они всегда являются достоверными. Это справедливо как для встроенных, так и для пользовательских типов. В частности, функции всегда соответствуют действительности:
>>> def func():
... pass
...
>>> bool(func)
True
Методы тоже всегда правдивы. Вы можете столкнуться с этим, если при вызове функции или метода не будут указаны круглые скобки:
>>> import datetime
>>> def before_noon():
... return datetime.datetime.now().hour < 12
...
>>> def greet():
... if before_noon:
... print("Good morning!")
... else:
... print("Good evening!")
...
>>> greet()
Good morning!
>>> datetime.datetime.now().hour
20
Это может произойти из-за забытой круглой скобки или вводящей в заблуждение документации, в которой не упоминается, что вам нужно вызвать функцию. Если вы ожидаете логическое значение Python, но у вас есть функция, которая возвращает логическое значение, то оно всегда будет верным.
По умолчанию пользовательские типы всегда соответствуют действительности:
>>> class Dummy:
... pass
...
>>> bool(Dummy())
True
Создание пустого класса делает каждый объект этого класса достоверным. Все объекты являются достоверными, если не определены специальные методы. Если вы хотите сделать некоторые экземпляры вашего класса ложными, вы можете определить .__bool__():
>>> class BoolLike:
... am_i_truthy = False
... def __bool__(self):
... return self.am_i_truthy
...
>>> x = BoolLike()
>>> bool(x)
False
>>> x.am_i_truthy = True
>>> bool(x)
True
Вы также можете использовать .__bool__(), чтобы сделать объект ни истинным, ни ложным:
>>> class ExcludedMiddle:
... def __bool__(self):
... raise ValueError("neither")
...
>>> x = ExcludedMiddle()
>>> bool(x)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __bool__
ValueError: neither
>>> if x:
... print("x is truthy")
... else:
... print("x is falsy")
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __bool__
ValueError: neither
Оператор if также использует .__bool__(). Это делается для оценки того, является ли объект истинным или ложным, что определяет, какую ветвь следует выполнить.
Если вы определяете метод __len__ для класса, то его экземпляры имеют значение len(). В этом случае логическое значение экземпляров будет ложным именно тогда, когда их длина равна 0:
>>> class DummyContainer:
... my_length = 0
... def __len__(self):
... return self.my_length
...
>>> x = DummyContainer()
>>> bool(x)
False
>>> x.my_length = 5
>>> bool(x)
True
В этом примере len(x) возвращает 0 перед назначением и 5 после. Однако обратное неверно. Определение .__bool__() не дает экземплярам длины:
>>> class AlwaysTrue:
... def __bool__(self):
... return True
...
>>> class AlwaysFalse:
... def __bool__(self):
... return False
...
>>> bool(AlwaysTrue()), bool(AlwaysFalse())
(True, False)
>>> len(AlwaysTrue())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: object of type 'AlwaysTrue' has no len()
>>> len(AlwaysFalse())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: object of type 'AlwaysFalse' has no len()
Определение .__bool__() не приводит к тому, что экземпляры любого из классов имеют len(). Когда определены как .__bool__(), так и .__len__(), приоритет имеет .__bool__():
>>> class BooleanContainer:
... def __len__(self):
... return 100
... def __bool__(self):
... return False
...
>>> x=BooleanContainer()
>>> len(x)
100
>>> bool(x)
False
Несмотря на то, что x имеет длину 100, это все равно неверно.
Пример: числовые массивы
Приведенный выше пример может показаться чем-то, что происходит только тогда, когда вы пишете класс, предназначенный для демонстрации крайних случаев в Python. Однако можно получить аналогичные результаты, используя одну из самых популярных библиотек на PyPI: NumPy.
Массивы, как и числа, являются ложными или истинными в зависимости от того, как они соотносятся с 0:
>>> from numpy import array
>>> x = array([0])
>>> len(x)
1
>>> bool(x)
False
Несмотря на то, что x имеет длину 1, оно все равно ложно, поскольку его значение равно 0.
Когда массивы содержат более одного элемента, некоторые элементы могут быть ложными, а некоторые - истинными. В таких случаях NumPy генерирует исключение:
>>> from numpy import array
>>> import textwrap
>>> y=array([0, 1])
>>> try:
... bool(y)
... except ValueError as exc:
... print("\n".join(textwrap.wrap(str(exc))))
...
The truth value of an array with more than one element is ambiguous.
Use a.any() or a.all()
Исключение настолько многословно, что для удобства чтения в коде используется текстовая обработка для переноса строк.
Еще более интересный граничный случай связан с пустыми массивами. Вы можете задаться вопросом, являются ли они ложными, как другие последовательности, или истинными, потому что они не равны 0. Как вы видели выше, это не единственные два возможных ответа. Массивы также могут не принимать логическое значение.
Интересно, что ни один из этих вариантов не является полностью верным:
>>> bool(array([]))
<stdin>:1: DeprecationWarning: The truth value of an empty array is ambiguous.
Returning False, but in future this will result in an error.
Use `array.size > 0` to check that an array is not empty.
False
В то время как пустые массивы являются в настоящее время ложными, полагаться на такое поведение опасно. В какой-нибудь будущей версии NumPy это вызовет исключение.
Операторы и функции
В Python есть еще несколько мест, где выполняется логическое тестирование. Одно из них - логические операторы.
Операторы and, or, и not принимают любое значение, поддерживающее логическую проверку. В случае not он всегда будет возвращать логическое значение:
>>> not 1
False
>>> not 0
True
Таблица истинности для not по-прежнему верна, но теперь она принимает истинность входных данных.
В случае and и or, в дополнение к оценке короткого замыкания, они также возвращают значение, при котором они прекратили оценку:
>>> 1 and 2
2
>>> 0 and 1
0
>>> 1 or 2
1
>>> 0 or 2
2
Таблицы истинности по-прежнему верны, но теперь они определяют достоверность результатов, которая зависит от достоверности входных данных. Это может пригодиться, например, когда вы хотите задать значения по умолчанию.
Предположим, у вас есть функция с именем summarize(), которая, если текст слишком длинный, берет начало и конец и добавляет многоточие (...) в середине. Это может быть полезно в некоторых отчетах, в которые не помещается полный текст. Однако в некоторых наборах данных отсутствуют значения, представленные None.
Поскольку summarize() предполагает, что входные данные являются строкой, при None:
>>> def summarize(long_text):
... if len(long_text) <= 4:
... return long_text
... return long_text[:2] +"..." + long_text[-2:]
...
>>> summarize("hello world")
'he...ld'
>>> summarize("hi")
'hi'
>>> summarize("")
''
>>> summarize(None)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in summarize
TypeError: object of type 'NoneType' has no len()
>>> for a in ["hello world", "hi", "", None]:
... print("-->", summarize(a or ""))
...
--> he...ld
--> hi
-->
-->
В этом примере используется ложность None и тот факт, что or не только приводит к короткому замыканию, но и возвращает последнее вычисляемое значение. Код для печати отчета добавляет or "" к аргументу summarize(). Добавление or "" поможет вам избежать ошибок с помощью всего лишь небольшого изменения кода.
Встроенные функции all() и any() оценивают достоверность, а также замыкают-circuit, но они не возвращают последнее вычисляемое значение. all() проверяет, все ли его аргументы соответствуют действительности:
>>> all([1, 2, 3])
True
>>> all([0, 1, 2])
False
>>> all(x / (x - 1) for x in [0, 1])
False
В последней строке all() не вычисляет x / (x - 1) для 1. Поскольку 1 - 1 равно 0, это вызвало бы ZeroDivisionError.
any() проверяет, является ли какой-либо из его аргументов истинным:
>>> any([1, 0, 0])
True
>>> any([False, 0, 0.0])
False
>>> any(1 / x for x in [1, 0])
True
В последней строке any() не вычисляет 1 / x для 0.
Заключение
Логический тип данных Python - это широко используемый тип данных во многих полезных приложениях. Вы можете использовать логические значения с операторами типа not, and, or, in, is, ==, и != для сравнения значений и проверки на принадлежность, идентичность или равенство. Вы также можете использовать логическое тестирование с помощью инструкции if, чтобы управлять ходом выполнения ваших программ на основе достоверности выражения.
В этом руководстве вы узнали, как:
- Манипулировать логическими значениями с помощью Логических операторов
- Преобразовать логические значения в другие типы
- Преобразовать другие типы в Логические значения Python
- Используйте логические значения для написания эффективного и удобочитаемого Кода на Python
Теперь вы знаете, как работает вычисление короткого замыкания, и понимаете связь между логическими значениями и оператором if. Эти знания помогут вам как понять существующий код, так и избежать распространенных ошибок, которые могут привести к ошибкам в ваших собственных программах.