Числа, логические операторы и не только
Однажды, решая задачи на Codewars.com, наткнулся на интересное решение.
Суть задачи: на входе вводилось целое число, далее нужно было сложить все его цифры. Если получалось не однозначное число, то снова сложить его цифры, и так далее пока не получим однозначное. Его и нужно было вывести.
In: 23456798 Out: 8 (23456789 -> 2+3+4+5+6+7+9+8 = 44 -> 4+4 = 8)
Самое короткое решение из предложенных было:
def digital_root(n): return n % 9 or n and 9
Здесь применяется свойство: у суммы цифр любого числа будет такой же остаток при делении на 9, как и у самого числа. Но заинтересовало не это, а именно взаимодействие логических операторов и чисел. Итак, давайте разберем пару примеров.
Множественные сравнения
Мало для кого секрет, что существует форма записи сравнения нескольких чисел в цепочку: a < b < c
.
Если условия удовлетворяют сравнению, то получаем True
, если нет – False
. Казалось бы, причем тут логические операторы? Но если погрузимся вглубь, то станет понятно, что эта запись – не что иное, как сочетание операторов сравнения и логических операторов.
Запись a < b < c
равносильна a < b and b < c
. Причем операторы сравнения могут быть любыми, а вместо a
, b
, c
могут быть любые объекты, которые сравниваем в любом количестве.
print(10 < 20 < 30) # True
print(10 < (5 * 3) < 20) # True
print(10 < sum([1, 10, 3, 2]) < 20) # True
Всё достаточно ясно и понятно. Но тут можно попасться в ловушку.
Если мы запишем первое выражение как:
print(10 < 20 < 30 == True) # False
Казалось бы, почему? И тут опять вспоминаем как работают множественные сравнения. Представим запись в виде, в каком ее обрабатывает Python.
10 < 20 < 30 == True
равносильно 10 < 20 and 20 < 30 and 30 == True
.
И если в первых двух сравнения мы получим True
, то при сравнении 30 == True
мы получаем False
(тип boolean является подклассом integer в Python и, при сравнении int
и bool
, True
принимает значение 1
, а False
– 0
).
Не путать с bool(число)
! – здесь любое ненулевое число выдаст True
.
Если нам нужно сохранить данный формат записи и получить корректный ответ, то на помощь приходят скобки:
print((10 < 20 < 30) == True) # True
Что равносильно: (10 < 20 and 20 < 30) == True
.
Вроде разница между (10 < 20 < 30) == True
и 10 < 20 < 30 == True
неочевидная, но результаты получаем противоположные, что может нам испортить, как минимум, настроение, когда получим не то, что ожидали.
Числа и логические операторы
def digital_root(n): return n % 9 or n and 9
Что же означает запись n % 9 or n and 9
? Чтобы ответить на этот вопрос, разберемся, как работают логические операторы or
, and
и not
в Python:
x or y
: Еслиx
ложно, возвращаетy
, иначе возвращаетx
x and y
: Еслиx
ложно, возвращаетx
, иначе возвращаетy
not x
: Еслиx
ложно, возвращаетTrue
, иначе возвращаетFalse
False or True
вернетTrue
, так как первый аргумент ложный, возвращает 2й.True or False
вернетTrue
, так как первый аргумент не ложный, возвращает его.True or True
вернетTrue
, так как первый аргумент не ложный, возвращает его.False or False
вернетFalse
, так как первый аргумент ложный, возвращает 2й.False and True
вернетFalse
, так как первый аргумент ложный, возвращает его.True and False
вернетFalse
, так как первый аргумент не ложный, возвращает второй.True and True
вернетTrue
, так как первый аргумент не ложный, возвращает 2й.False and False
вернетFalse
, так как первый аргумент ложный, возвращает его.
С not x
, думаю, все и так понятно.
Вот мы и подошли к ответу на наш вопрос. Логические операторы работают подобным образом не только с булевыми переменными, но и с любыми другими объектами:
print(0 and 5) # 0
print(9 and 5) # 5
print(5 or []) # 5
print(5 and []) # []
print(() or {}) # {}
print(None or [1, 2, 3, 4][1:3]) # [2, 3]
Итак, что вернет ввод n % 9 or n and 9
?
Если число кратно 9 (т.е. имеет остаток 0 при делении на 9), то:
18369 -> 27 -> 9
(число 9 и так однозначное, поэтому на не надо дальше получать остаток от деления на 9).
32054 -> 14 -> 5
. Действительно, 32054 % 9 = 5.
Вот так ёмко и элегантно можно решить задачу в одну строку без циклов, больших ветвей условий и рекурсий, если знать математические правила и как работают логические операторы в Python.
Автор: Руслан Шакиров
👉🏻Подписывайтесь на PythonTalk в Telegram 👈🏻