Python: Маленькая хитрость. if-else vs and-or.
На этот раз будет мини-заметка, уж слишком мне понравился трюк, о котором пойдёт речь дальше :).
Наверное любому программисту приходится часто писать конструкции такого вида:
if a == b:
c = d
else:
c = e
Как-то разбирая исходники какого-то проекта наткнулся на весьма компактную замену. Вот для сравнения два аналогичных блока кода с использованием логических операций python’а и с использованием конструкции if-else:
# if-else:
if random.random() > 0.5:
a = 1
else:
a = 0
# and-or style:
a = random.random() > 0.5 and 1 or 0
На мой взгляд выглядит гораздо лучше и компактнее (да-да, if-else можно записать в 2 строчки вместо 4, но мне так не нравится ^__^) . Другое дело, что некоторым это может показаться противоречиво с некоторыми пунктами дзена python’а (import this), но это спорно :). В общем использую там, где считаю нужным.
Кстати, если кого-то интересует производительность, то она примерно одинакова, далее выложу тесты и объяснение логики для тех, кому интересно.
>>> import timeit
>>> t1 = timeit.Timer('if random.random()>0.5:a=1\nelse:a =0', 'from __main__ import a\nimport random')
>>> t2 = timeit.Timer('a=random.random()>0.5 and 1 or 0', 'from __main__ import a\nimport random')
>>> t1.repeat()
[0.21074632467116317, 0.2097615509806019, 0.20978293771349854]
>>> t2.repeat()
[0.21944338095772764, 0.21785226684687586, 0.21940797240040411]
Тесты привёл, теперь объяснение.
Дело в том, что логические операции and и or в python’е возвращают один из операндов. Операция and возвращает первый операнд, если он приводится к логическому типу False, иначе – второй. Операция or наоборот, возвращает 1й операнд, если он приводится к типу True, иначе – второй. Всё это связано с «ленивостью» (lazy) операций, вычисляется столько аргументов, сколько надо для того, чтобы однозначно определить результат (например, 1 or x всегда даёт True, поэтому x не вычисляется).


Не следует забывать о том, что вот такая конструкция отработает правильно:
a = random.random() > 0.5 and 1 or 0
А вот такая конструкция всегда будет возвращать устанавливать a=1
a = random.random() <= 0.5 and 0 or 1
@Сергей
Верно, для этого я кратко написал про ленивость операций, но спасибо за замечание. Просто увидев такой вариант был сильно удивлён что до такого кто-то додумался :).
Да и злоупотреблять такими конструкциями тоже не стоит, они сокращают код, но понижают его читаемость :), это всё же именно «трюк».
Но если задача будет примерно такой: http://clck.ru/0faG то это будет отличный вариант).