Использование кортежей вместо булевых операторов


Есть ли разница (с точки зрения производительности, а не читаемости) между следующими способами оценки двух (или более) условий, связанных с помощью and?

a == 1 and b == 2

Или

(a,b) == (1,2)


То же самое для or:

a == 1 or b == 1

Или

1 in (a,b)


Какой путь предпочтительнее? Тот, который использует булевы операторы, или другой, который использует кортежи?

3 2

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 .. 

В случае литералов это, очевидно, не имеет никакого значения для потока, и любые различия в производительности, вероятно, будут незначительными. В данном случае я предпочитаю второй стиль.