Числа, логические операторы и не только
Однажды, решая задачи на 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, иначе возвращаетxx and y: Еслиxложно, возвращаетx, иначе возвращаетynot 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 👈🏻