Использование кортежей вместо булевых операторов
Есть ли разница (с точки зрения производительности, а не читаемости) между следующими способами оценки двух (или более) условий, связанных с помощью and
?
a == 1 and b == 2
Или
(a,b) == (1,2)
То же самое для or
:
a == 1 or b == 1
Или
1 in (a,b)
Какой путь предпочтительнее? Тот, который использует булевы операторы, или другой, который использует кортежи?
3 ответа:
Использование операторов и/или почти всегда лучше с точки зрения производительности (возможно, не для удобства чтения, но это другая проблема): 1. Они избегают необходимости создавать объекты кортежей, как вы делаете в других примерах, которые будут более дорогостоящими с точки зрения пространственно-временной сложности 2. Они имеют короткое замыкание, что означает, что есть хороший шанс избежать выполнения ненужных частей кода.
Это некоторые тайминги, использующие
python 2.7
In [29]: a = 1 In [30]: b = 2 In [31]: timeit a == 1 and b == 2 10000000 loops, best of 3: 82.2 ns per loop In [32]: timeit (a,b) == (1,2) 10000000 loops, best of 3: 132 ns per loop In [33]: timeit 1 in (a,b) 10000000 loops, best of 3: 118 ns per loop
И когда результат ложен:
In [37]: timeit a == 2 and b == 2 10000000 loops, best of 3: 52.2 ns per loop In [38]: timeit 3 in (a,b) 10000000 loops, best of 3: 151 ns per loop In [39]: timeit (a,b) == (2,2) 10000000 loops, best of 3: 144 ns per loop
a == 2 and b == 2
быстрее, так какand
является оператором короткого замыканияСогласно комментарию Вима, использование набора будет быстрее, чем проверка кортежа на принадлежность:
In [55]: timeit 3 in {a,b} 10000000 loops, best of 3: 92.9 ns per loop
Да, есть логическая разница в том, что использование
and
/or
бывают короткие замыкания.>>> def return1(): ... return 1 ... >>> def raises(): ... raise Exception('uh oh') ... >>> a, b = 1, 2 >>> a == return1() or b == raises() True >>> b == return1() and a == raises() False
Обратите внимание, что функция, вызывающая исключение, никогда не вызывается. В обоих других примерах мы будем иметь необработанное исключение:
>>> a, b == return1(), raises() # raises Exception >>> 1 in (return1(), raises()) # raises Exception ..
В случае литералов это, очевидно, не имеет никакого значения для потока, и любые различия в производительности, вероятно, будут незначительными. В данном случае я предпочитаю второй стиль.