Agreed that 1 <= len(vals) < 5 would be more Pythonic.
Agreed that 1 <= len(vals) < 5 would be more Pythonic.
"x in range(10)" will operate in constant time and memory in Python 3. Whether it is actually more efficient than "0 <= x < 10" is another matter, of course. I would expect the call to range() to dominate here, and indeed, it is much slower in this microbenchmark:
$ python3 -m timeit -s 'x = 8' 'x in range(10)'
1000000 loops, best of 3: 0.351 usec per loop
$ python3 -m timeit -s 'x = 8' '0 <= x < 10'
10000000 loops, best of 3: 0.0732 usec per loop
Even aside from this, I find the "0 <= x < 10" syntax to be clearer.From a formal CS perspective, that's why this is wrong; It's because 'in' is an o(n) operation, while the two comparisons are constant time. It may be abstracted away by the range object overloading, but that's a fairly narrow optimization it's able to pull off.
On modern CPUS, branching operations like compare usually are your problem; An unconditional function call is easy to optimize, but a branch in a looping construct is guaranteed to lead to one or more mispredictions. Trying to minimize the surface area of comparisons is an important part of performant code.
Also, as noted in other comments, range object has special cased O(1) implementation of in for integers (range_contains_long() in Objects/rangeobject.c)
CPython bytecode interpreter tries to minimize amount of branching it causes by using some non-obvious tricks, but still by definition it is going to cause at least one essentially unpredictable branch per interpreted instruction, so optimizing for number of branches in user python code is mostly pointless endeavor.
python3 -m timeit -s 'x = 8' 'x in range(1000)'
1000000 loops, best of 3: 0.405 usec per loop
python3 -m timeit -s 'x = 8' '0 <= x < 1000'
10000000 loops, best of 3: 0.0742 usec per loop
python3 -m timeit -s 'x = 8' 'x in range(100000)'
1000000 loops, best of 3: 0.405 usec per loop
python3 -m timeit -s 'x = 1123' 'x in range(100000)'
1000000 loops, best of 3: 0.415 usec per loop
python3 -m timeit -s 'x = 1123' '0 <= x < 100000'
10000000 loops, best of 3: 0.0745 usec per loop
While 0<=x<10 seems to be 5 times faster, both seem to be independent of the actual chosen values.