Problem:
Write a Python function, clip(lo, x, hi) that returns lo if x is less than lo; hi if x is greater than hi; and x otherwise. For this problem, y
So you have a number of options proposed so far. Not yet posted is the nested ternary expression:
def clip(lo, x, hi):
return lo if x <= lo else hi if x >= hi else x
But since this uses explicit conditional tests, probably not suitable as a solution to the original question. Still, given these options, this is the one that actually has the advantages of short-circuiting if x <= lo
(all other methods evaluate all comparisons and/or perform one or two method calls). Let's see how these alternatives actually perform using timeit (tested with Python 3.3, so range does not build a list, but returns an iterator):
python -m timeit -s "lo,hi=10,90" "[max(lo,min(hi,x)) for x in range(100)]"
10000 loops, best of 3: 54.5 usec per loop
(2 function calls per evaluation, kills performance)
python -m timeit -s "lo,hi=10,90" "[(lo,(hi,x)[xlo] for x in range(100)]"
10000 loops, best of 3: 40.9 usec per loop
(evaluates both tests and builds tuples for every evaluation, but at least no function calls)
python -m timeit -s "lo,hi=10,90" "[sorted((lo,x,hi))[1] for x in range(100)]"
10000 loops, best of 3: 90.5 usec per loop
(builds tuple and sorts - sorry, Gnibbler, this is the slowest)
python -m timeit -s "lo,hi=10,90" "[lo if x <= lo else hi if x >= hi else x for x in range(100)]"
100000 loops, best of 3: 18.9 usec per loop
(fastest, no function calls, only evaluates x >= hi
if x > lo
)
This short-circuiting can be seen if you move the value of lo to much higher in the test range:
python -m timeit -s "lo,hi=80,90" "[lo if x <= lo else hi if x >= hi else x for x in range(100)]"
100000 loops, best of 3: 15.1 usec per loop
(If you want to reproduce these under Python 2.x, replace range
with xrange
.)