Перевод статьи “Telling The Truthy”.
Истина и ложь в Python – это не только булевы True и False. Я всегда говорю правду. Честно! Мне даже детям неловко врать.
«А Дед Мороз правда существует?»
«А как ты думаешь? Я никогда его не видел, но в новогоднее утро под елкой лежат подарки. Как, по-твоему, они туда попали?»
Как видите, в моем ответе нет ничего неправдивого.
А иногда преподаватели говорят ученикам неправду – не потому, что хотят обмануть или обмануть их, а потому, что правда слишком сложна. Более простую «неправду» легче объяснить.
Когда я преподаю на курсах для начинающих и ввожу оператор if
или цикл while
, я использую такие фразы, как:
«За if
должно следовать что-то, что Python понимает как истину или ложь».
Конечно, проще сказать, что за if
должно следовать True или False. Но это было бы неправдой. За ключевым словом if
может следовать любой объект или выражение. Выражение оценивается в объект, а Python может «рассматривать любой объект как истинный или ложный», если использовать его в булевом контексте.
Истинность и ложность
Начнем со встроенной функции bool()
, с помощью которой вы можете преобразовать любой объект в булево значение.
Мы склонны думать о bool()
как о функции. Однако bool
– это имя класса. Поэтому bool()
– это конструктор, который создает экземпляр bool
из своего аргумента. Но это всего лишь деталь, и она не важна для нашего обсуждения. Поэтому давайте двигаться дальше.
Чтобы убедиться в этом, рассмотрим несколько коротких примеров (прим. ред.: здесь и далее код выполняется в консоли):
if 5: print("Yay") else: print("Nay") # Yay if 0: print("Yay") else: print("Nay") # Nay bool(5) # True bool(0) # False
Любое целое число, кроме 0, преобразуется в True, когда вы приводите его к булевым значениям с помощью функции bool()
. Таким образом, каждое ненулевое целое число является истинным, а 0 – ложным. Мы можем распространить эту идею на все числовые типы, а не только на целые числа. Например, 0.0 является ложным, а любое другое значение типа float – истинным.
Когда Python нужно понять, истинен или ложен объект, он будет использовать значение, возвращаемое функцией bool()
.
Для объектов, имеющих длину (такие объекты называются размерными), объект является ложным, если его длина равна 0, и истинным для любой ненулевой длины:
if [1, 2]: print("Yay") else: print("Nay") # Yay if []: print("Yay") else: print("Nay") # Nay len([1, 2]) # 2 bool([1, 2]) # True len([]) # 0 bool([]) # False
Вы можете попробовать это с другими размерными объектами, такими как строки, кортежи, словари и множества. Эти структуры данных являются ложными, если они пусты, и истинными, если непусты.
И есть еще один объект, который всегда ложный: None
.
Истинность, ложность и определяемые пользователем классы
Как насчет других объектов, которые не являются числовыми и не имеют длины?
Давайте создадим простой класс и протестируем его:
class TestTruthiness: pass test = TestTruthiness() if test: print("Yay") else: print("Nay") # Yay bool(test) # True
Примечание редакции: о pass
читайте в статье “Ключевое слово pass в Python”.
Каждый объект по умолчанию является истинным. Однако это поведение по умолчанию можно отменить. Вы можете определить истинность или ложность объекта, определив специальный метод __bool__()
в определении класса:
class TestTruthiness: def __bool__(self): return False test = TestTruthiness() if test: print("Yay") else: print("Nay") # Nay bool(test) # False
Теперь у класса есть специальный метод __bool__()
. В данном примере этот специальный метод всегда возвращает False. Теперь все экземпляры этого класса будут ложными.
Специальный метод __bool__()
, конечно же, может содержать дополнительную логику:
class TestTruthiness: def __init__(self, person): self.person = person def __bool__(self): return self.person == "Stephen" bool(TestTruthiness("Stephen")) # True bool(TestTruthiness("Jane")) # False
Мы добавили специальный метод __init__()
и атрибут данных .person
. Теперь экземпляры этого класса будут истинными, если человека зовут «Stephen», и ложными для любого другого имени.
Но есть и другой способ контролировать истинность или ложность, не определяя специальный метод __bool__()
. Вы можете определить метод __len__()
, который делает объекты размерными – у них появляется длина:
class TestTruthiness: def __init__(self, person): self.person = person def __len__(self): return len(self.person) bool(TestTruthiness("Stephen")) # True bool(TestTruthiness("Jane")) # True bool(TestTruthiness("")) # False
Вы установили длину объекта TestTruthiness
равной значению его атрибута данных .person
– в данном случае это единственный атрибут, который есть у этого класса!
В этом примере объекты, представляющие людей с именами «Stephen» и «Jane», оба истинны. Все имена истинны. Но если вместо имени человека используется пустая строка, объект TestTruthiness
становится ложным.
Некоторые другие «неожиданные» следствия истинности и ложности
Позвольте мне начать с примера:
first_list = [] second_list = [5, 10] if first_list or second_list: print("At least one of the lists is not empty") # At least one of the lists is not empty
Что в этом неожиданного? Мы видели, что first_list
является ложным, поскольку это пустой список. А second_list
является истинным, поскольку он не пуст. Вы используете выражение с or
в предложении if
, и поскольку один из списков является истинным, блок if
выполняется.
Но что возвращает выражение first_list or second_list
? Нет, оно не возвращает True:
first_list or second_list # [5, 10]
Выражение начинается с first_list
, что является ложным. Выражение or
ищет хотя бы одно истинное значение. Поэтому оно переходит к объекту после ключевого слова or
, second_list
. И это значение истинно. Поэтому выражение or
возвращает этот объект. Ему не нужно возвращать True, поскольку оно возвращает истинное значение, и этого достаточно!
А что, если истинностный объект находится перед ключевым словом or
?
second_list or first_list # [5, 10]
Вы получите тот же результат. Но в этом случае первый объект является истинным, а выражению or
нужен только один истинный объект. Поэтому выражению or
незачем смотреть, что идет после ключевого слова or
.
Вам нужно доказательство?
second_list or int("hello") # [5, 10]
И что здесь странного? Выражение после or
должно вызывать ошибку:
int("hello") # Traceback (most recent call last): # File "<input>", line 1, in <module> # ValueError: invalid literal for int() with base 10: 'hello'
Строка “hello” не может быть преобразована в целое число. Но когда int("hello")
используется после ключевого слова or
в second_list
или int("hello")
, выражение после or
так и не выполняется.
А что произойдет, если оба объекта в выражении or
будут ложными?
0 or [] # []
Первый объект проверяется первым. Целое число 0 является ложным. Выражение ищет хотя бы один истинный объект. Поэтому проверяется второй объект. Пустой список []
также является ложным. Но выражению не нужно возвращать False. Вместо этого оно возвращает последний элемент, который является ложным. Этого вполне достаточно в любом булевом контексте Python.
Давайте подведем итог:
- Выражение
or
возвращает первый объект, если он истинный. - Но если первый объект ложный, возвращается второй объект, каким бы он ни был. Если второй объект ложный, то и все выражение ложное. Но если второй объект истинный, то и все выражение истинное.
Аналогичную логику мы можем проследить и в случае с and
. Выражение and
требует, чтобы оба объекта были истинными. Поэтому, если первый объект ложен, выражение сразу же возвращает этот первый объект, не оценивая второй:
0 and 10 # 0 0 and int("hello") # 0
Во втором из этих примеров ошибка не возникает, поскольку первый объект является ложным и возвращается сразу.
10 and int("hello") # Traceback (most recent call last): # File "<input>", line 1, in <module> # ValueError: invalid literal for int() with base 10: 'hello'
Но когда первый объект истинный, выражение and
переходит к тому, что идет после ключевого слова and
. Это приводит к ошибке.
Если в выражении and
первый объект истинный, то всегда возвращается второй объект, независимо от того, истинный он или ложный:
10 and [2, 3] # [2, 3] 10 and [] # []
Такое поведение с or
и and
известно как вычисление по короткой схеме. Теперь ваша очередь поиграть с выражениями or
и and
, а также с истинными и ложными объектами.
И некоторые факты о True и False
Эта статья не о типе данных Boolean и его двух экземплярах, True и False. Но в заключение позвольте мне рассказать немного фактов об этих объектах.
True + True # 2 25 * False # 0 some_dict = {1: "This is the integer 1", True: "This is the Boolean True"} some_dict[1] # 'This is the Boolean True'
Класс bool
является подклассом int
. Поэтому True равно 1, а False равно 0. Вот почему True + True
равно 1 + 1 (и мне не нужно говорить вам, что это дает 2). Умножение на False – то же самое, что умножение на 0. А поскольку словари должны иметь уникальные ключи, при создании ключа True
он отменяет значение предыдущего ключа 1
, поскольку True равен 1.
Обратите внимание, что True и 1 – это не один и тот же объект, но они имеют одно и то же значение. True = 1
дает False, что говорит о том, что они не являются одним и тем же объектом. Но True == 1
дает True, что показывает, что они равны.
Заключительные слова
Насколько сложным может быть понятие true или false? Если расширить определение истинности и ложности, то можно обнаружить нечто большее, чем кажется на первый взгляд.
Настоящим я подтверждаю, что, насколько мне известно, в этой статье я предоставил вам правду, всю правду и ничего, кроме правды.