How to clamp an integer to some range?

我的未来我决定 提交于 2019-12-17 04:29:20

问题


I have the following code:

new_index = index + offset
if new_index < 0:
    new_index = 0
if new_index >= len(mylist):
    new_index = len(mylist) - 1
return mylist[new_index]

Basically, I calculate a new index and use that to find some element from a list. In order to make sure the index is inside the bounds of the list, I needed to write those 2 if statements spread into 4 lines. That's quite verbose, a bit ugly... Dare I say, it's quite un-pythonic.

Is there any other simpler and more compact solution? (and more pythonic)

Yes, i know I can use if else in one line, but it is not readable:

new_index = 0 if new_index < 0 else len(mylist) - 1 if new_index >= len(mylist) else new_index

I also know I can chain max() and min() together. It's more compact, but I feel it's kinda obscure, more difficult to find bugs if I type it wrong. In other words, I don't find it very straightforward.

new_index = max(0, min(new_index, len(mylist)-1))

回答1:


This is pretty clear, actually. Many folks learn it quickly. You can use a comment to help them.

new_index = max(0, min(new_index, len(mylist)-1))



回答2:


sorted((minval, value, maxval))[1]

for example:

>>> minval=3
>>> maxval=7
>>> for value in range(10):
...   print sorted((minval, value, maxval))[1]
... 
3
3
3
3
4
5
6
7
7
7



回答3:


See numpy.clip:

index = numpy.clip(index, 0, len(my_list) - 1)



回答4:


many interesting answers here, all about the same, except... which one's faster?

import numpy
np_clip = numpy.clip
mm_clip = lambda x, l, u: max(l, min(u, x))
s_clip = lambda x, l, u: sorted((x, l, u))[1]
py_clip = lambda x, l, u: l if x < l else u if x > u else x
>>> import random
>>> rrange = random.randrange
>>> %timeit mm_clip(rrange(100), 10, 90)
1000000 loops, best of 3: 1.02 µs per loop

>>> %timeit s_clip(rrange(100), 10, 90)
1000000 loops, best of 3: 1.21 µs per loop

>>> %timeit np_clip(rrange(100), 10, 90)
100000 loops, best of 3: 6.12 µs per loop

>>> %timeit py_clip(rrange(100), 10, 90)
1000000 loops, best of 3: 783 ns per loop

paxdiablo has it!, use plain ol' python. The numpy version is, perhaps not surprisingly, the slowest of the lot. Probably because it's looking for arrays, where the other versions just order their arguments.




回答5:


Chaining max() and min() together is the normal idiom I've seen. If you find it hard to read, write a helper function to encapsulate the operation:

def clamp(minimum, x, maximum):
    return max(minimum, min(x, maximum))



回答6:


Whatever happened to my beloved readable Python language? :-)

Seriously, just make it a function:

def addInRange(val, add, minval, maxval):
    newval = val + add
    if newval < minval: return minval
    if newval > maxval: return maxval
    return newval

then just call it with something like:

val = addInRange(val, 7, 0, 42)

Or a simpler, more flexible, solution where you do the calculation yourself:

def restrict(val, minval, maxval):
    if val < minval: return minval
    if val > maxval: return maxval
    return val

x = restrict(x+10, 0, 42)

If you wanted to, you could even make the min/max a list so it looks more "mathematically pure":

x = restrict(val+7, [0, 42])



回答7:


This one seems more pythonic to me:

>>> def clip(val, min_, max_):
...     return min_ if val < min_ else max_ if val > max_ else val

A few tests:

>>> clip(5, 2, 7)
5
>>> clip(1, 2, 7)
2
>>> clip(8, 2, 7)
7



回答8:


If your code seems too unwieldy, a function might help:

def clamp(minvalue, value, maxvalue):
    return max(minvalue, min(value, maxvalue))

new_index = clamp(0, new_index, len(mylist)-1)



回答9:


Avoid writing functions for such small tasks, unless you apply them often, as it will clutter up your code.

for individual values:

min(clamp_max, max(clamp_min, value))

for lists of values:

map(lambda x: min(clamp_max, max(clamp_min, x)), values)


来源:https://stackoverflow.com/questions/4092528/how-to-clamp-an-integer-to-some-range

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!