What is the best approach in python: multiple OR or IN in an if statement?

What is the best approach in python: multiple OR or IN in an if statement? Considering performance and best grades.

if cond == '1' or cond == '2' or cond == '3' or cond == '4' (etc...) : 

OR

 if cond in ['1','2','3','4']: 

Thanks.

+7
performance python design-patterns
source share
3 answers

A better approach is to use a set:

 if cond in {'1','2','3','4'}: 

since recruiting membership testing is O (1) (constant cost).

The other two approaches are the same in complexity; just the difference in fixed costs. Both the in test in the list and the short circuit of the or ; end as soon as a match is found. One uses a byte code jump sequence (skip to the end if True ), and the other uses a C-loop and early exit if the value matches. In the worst case, when cond does not match an element in the sequence, or the approach must check all the elements before it can return False . Of the two, I would choose the test in any day, because it is much more readable.

+14
source share

Pieters answer is best in most cases. However, in your particular case, I would not use in or or , but instead I will do the following:

 if 0 < int(cond) < 5: 

If cond is '1', '2', '3' or '4', the if block will execute. The good thing about this is that it is shorter than the other answers.

+2
source share

It really depends on the version of Python. In Python 2.7 , there were no constants specified in the bytecode, so in Python 2, for a fixed constant, a small set of values ​​uses a tuple:

 if x in ('2', '3', '5', '7'): ... 

A tuple is a constant:

 >>> dis.dis(lambda: item in ('1','2','3','4')) 1 0 LOAD_GLOBAL 0 (item) 3 LOAD_CONST 5 (('1', '2', '3', '4')) 6 COMPARE_OP 6 (in) 9 RETURN_VALUE 

Python is also smart enough to optimize the list of constants in Python 2.7 for a tuple:

 >>> dis.dis(lambda: item in ['1','2','3','4']) 1 0 LOAD_GLOBAL 0 (item) 3 LOAD_CONST 5 (('1', '2', '3', '4')) 6 COMPARE_OP 6 (in) 9 RETURN_VALUE 

But Python 2.7 bytecode (and the compiler) does not support persistent set support:

 >>> dis.dis(lambda: item in {'1','2','3','4'}) 1 0 LOAD_GLOBAL 0 (item) 3 LOAD_CONST 1 ('1') 6 LOAD_CONST 2 ('2') 9 LOAD_CONST 3 ('3') 12 LOAD_CONST 4 ('4') 15 BUILD_SET 4 18 COMPARE_OP 6 (in) 21 RETURN_VALUE 

This means that the condition in if needs to be rebuilt for each test. .


However, in Python 3.4, bytecode supports the given constants; there the code is evaluated as follows:

 >>> dis.dis(lambda: item in {'1','2','3','4'}) 1 0 LOAD_GLOBAL 0 (item) 3 LOAD_CONST 5 (frozenset({'4', '2', '1', '3'})) 6 COMPARE_OP 6 (in) 9 RETURN_VALUE 

As for multi- or code, it produces completely disgusting bytecode:

 >>> dis.dis(lambda: item == '1' or item == '2' or item == '3' or item == '4') 1 0 LOAD_GLOBAL 0 (item) 3 LOAD_CONST 1 ('1') 6 COMPARE_OP 2 (==) 9 JUMP_IF_TRUE_OR_POP 45 12 LOAD_GLOBAL 0 (item) 15 LOAD_CONST 2 ('2') 18 COMPARE_OP 2 (==) 21 JUMP_IF_TRUE_OR_POP 45 24 LOAD_GLOBAL 0 (item) 27 LOAD_CONST 3 ('3') 30 COMPARE_OP 2 (==) 33 JUMP_IF_TRUE_OR_POP 45 36 LOAD_GLOBAL 0 (item) 39 LOAD_CONST 4 ('4') 42 COMPARE_OP 2 (==) >> 45 RETURN_VALUE 
+2
source share

All Articles