python - Tuple or list when using 'in' in an 'if' clause? -
which approach better? using tuple, like:
if number in (1, 2):
or list, like:
if number in [1, 2]:
which 1 recommended such uses , why (both logical , performance wise)?
the cpython interpreter replaces second form first.
that's because loading tuple constant 1 operation, list 3 operations; load 2 integer contents , build new list object.
because using list literal isn't otherwise reachable, substituted tuple:
>>> import dis >>> dis.dis(compile('number in [1, 2]', '<stdin>', 'eval')) 1 0 load_name 0 (number) 3 load_const 2 ((1, 2)) 6 compare_op 6 (in) 9 return_value
here second bytecode loads (1, 2)
tuple constant, in one step. compare creating list object not used in membership test:
>>> dis.dis(compile('[1, 2]', '<stdin>', 'eval')) 1 0 load_const 0 (1) 3 load_const 1 (2) 6 build_list 2 9 return_value
here n+1 steps required list object of length n.
this substitution cpython-specific peephole optimisation; see python/peephole.c
source. other python implementations then, want stick immutable objects instead.
that said, best option when using python 3.2 , up, use set literal:
if number in {1, 2}:
as peephole optimiser replace frozenset()
object , membership tests against sets o(1) constant operations:
>>> dis.dis(compile('number in {1, 2}', '<stdin>', 'eval')) 1 0 load_name 0 (number) 3 load_const 2 (frozenset({1, 2})) 6 compare_op 6 (in) 9 return_value
this optimization added in python 3.2 wasn't backported python 2.
as such, python 2 optimiser doesn't recognize option , cost of building either set
or frozenset
contents guaranteed more costly using tuple test.
set membership tests o(1) , fast; testing against tuple o(n) worst case. although testing agains set has calculate hash (higher constant cost), cost testing against tuple other first element going higher. on average, sets faster:
>>> import timeit >>> timeit.timeit('1 in (1, 3, 5)', number=10**7) # best-case tuples 0.21154764899984002 >>> timeit.timeit('8 in (1, 3, 5)', number=10**7) # worst-case tuples 0.5670104179880582 >>> timeit.timeit('1 in {1, 3, 5}', number=10**7) # average-case sets 0.2663505630043801 >>> timeit.timeit('8 in {1, 3, 5}', number=10**7) # worst-case sets 0.25939063701662235
Comments
Post a Comment