Earlier quoted context omitted.
This is in Python 3. xrange is now range, so only a generator is constructed. Agreed that 1 <= len(vals) < 5 would be more Pythonic.
To be pedantic (which I think is warranted here), range does not return a generator, it returns a sequence called a range object. This object can be indexed, sliced, and (relevant to this discussion) supports the 'in' operator. "x in range(10)" will operate in constant time and memory in Python 3. Whether it is actually more efficient than "0 $ python3 -m timeit -s 'x = 8' 'x in range(10)' 1000000 loops, best of 3: 0…
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.