На этот раз будет мини-заметка, уж слишком мне понравился трюк, о котором пойдёт речь дальше.
Наверное любому программисту приходится часто писать конструкции такого вида:
1 2 3 4 5 6 7 | if a == b: c = d else: c = e |
Как-то разбирая исходники какого-то проекта (скорее всего, это была Django) наткнулся на весьма компактную замену. Вот для сравнения два аналогичных блока кода с использованием логических операций python’а и с использованием конструкции if-else:
1 2 3 4 5 6 7 8 9 10 11 12 13 | # 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), но это спорно. В общем использую там, где считаю нужным.
Кстати, если кого-то интересует производительность, то она примерно одинакова, далее выложу тесты и объяснение логики для тех, кому интересно.
1 2 3 4 5 6 7 8 9 10 11 12 13 | >>>> 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 не вычисляется).
Update (17.02.2013): сквозь призму опыта, спустя достаточно большой промежуток времени, могу посоветовать оставить в стороне джедайские техники. Максимум, используйте только один оператор: x = y and z или x = y or z. Да и то, сначала подумайте, может if/else одной строкой будет наглядней? Хотя, конструкция «x = y if z else a» поддерживается только начинаю с Python 2.5, но я надеюсь вас это ограничение не затрагивает.