如何将整数钳位到某个范围?

Den*_*aia 81 python clamp

我有以下代码:

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]
Run Code Online (Sandbox Code Playgroud)

基本上,我计算一个新索引并使用它来从列表中查找一些元素.为了确保索引在列表的边界内,我需要将这两个if语句写成4行.那是相当冗长,有点难看......我敢说,这是非常不科幻的.

还有其他更简单,更紧凑的解决方案吗?(和更多pythonic)

是的,我知道我可以if else在一行中使用,但它不可读:

new_index = 0 if new_index < 0 else len(mylist) - 1 if new_index >= len(mylist) else new_index
Run Code Online (Sandbox Code Playgroud)

我也知道我可以链max()min()在一起.它更紧凑,但我觉得它有点模糊,如果我输错了就更难找到错误.换句话说,我觉得这很简单.

new_index = max(0, min(new_index, len(mylist)-1))
Run Code Online (Sandbox Code Playgroud)

S.L*_*ott 105

实际上,这很清楚.很多人很快就学会了.您可以使用评论来帮助他们.

new_index = max(0, min(new_index, len(mylist)-1))
Run Code Online (Sandbox Code Playgroud)

  • `def clamp(n,最小,最大):返回max(最小,最小(n,最大))` (35认同)
  • 虽然我觉得它不像它应该的那样是pythonic,但我也觉得这是我们现在拥有的最佳解决方案. (12认同)
  • @MateenUlhaq `utils.py` (5认同)
  • @csl Folks总是提供这些小帮助函数,但我永远不知道放在哪里.`helperFunctions.py`?一个单独的模块?如果这对于完全不同的东西充满各种"辅助功能"会怎么样? (2认同)
  • 我不知道,但是如果你收集了很多这些并将它们分类为合理的模块,为什么不放在 GitHub 上并从中创建一个 PyPi 包呢?说不定就会流行起来。 (2认同)
  • 我更喜欢重新排列它,使值按顺序排列:“min(max(0, new_index), len(mylist)-1)”。 (2认同)

Joh*_*ooy 78

sorted((minval, value, maxval))[1]
Run Code Online (Sandbox Code Playgroud)

例如:

>>> 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
Run Code Online (Sandbox Code Playgroud)

  • 非常有创意,实际上和`min(max())`结构一样快.在数量在范围内并且不需要交换的情况下,非常快一些. (9认同)
  • +1表示`sorted()`内置的创意用法.非常紧凑,但它只是有点模糊.无论如何,看到其他创意解决方案总是很好! (8认同)

Nei*_*l G 37

请参阅numpy.clip:

index = numpy.clip(index, 0, len(my_list) - 1)
Run Code Online (Sandbox Code Playgroud)

  • @RoryO'Kane:你试过吗? (11认同)

Sin*_*ion 34

这里有很多有趣的答案,除了......哪一个更快?

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
Run Code Online (Sandbox Code Playgroud)
>>> 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
Run Code Online (Sandbox Code Playgroud)

paxdiablo有它!,使用普通的ol'python.numpy版本也许并不奇怪,是最慢的版本.可能是因为它正在寻找数组,其他版本只是在命令它们的参数.

  • 考虑到Numpy的性能是围绕大型阵列设计的,而不是单个数字,所以@LenarHoyt并不令人惊讶.此外,它必须首先将整数转换为内部数据类型,并且因为它接受几种不同类型的输入,所以可能需要相当长的时间才能确定输入的类型以及将其转换为什么类型.如果您将数千个值的数组(最好不是列表或元组,它必须首先转换)提供给它,您将看到更好的Numpy性能. (6认同)
  • @DustinAndrews你已经倒退了.1μs是10 ^ -6秒,1 ns是10 ^ -9秒.python示例在0.784μs内完成1个循环.或者至少,它是在我测试它的机器上做的.该微基准标记与任何其他微基准标记一样有用; 它可以让你远离真正糟糕的想法,但可能无法帮助你找到实际编写*有用*代码的最快方法. (3认同)

Lau*_*ves 14

链接max()min()在一起是我见过的正常习语.如果您发现难以阅读,请编写一个辅助函数来封装操作:

def clamp(minimum, x, maximum):
    return max(minimum, min(x, maximum))
Run Code Online (Sandbox Code Playgroud)


pax*_*blo 13

无论我心爱的可读Python语言发生了什么?:-)

说真的,让它成为一个功能:

def addInRange(val, add, minval, maxval):
    newval = val + add
    if newval < minval: return minval
    if newval > maxval: return maxval
    return newval
Run Code Online (Sandbox Code Playgroud)

然后用以下内容调用它:

val = addInRange(val, 7, 0, 42)
Run Code Online (Sandbox Code Playgroud)

或者更简单,更灵活的解决方案,您可以自己进行计算:

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

x = restrict(x+10, 0, 42)
Run Code Online (Sandbox Code Playgroud)

如果你愿意,你甚至可以使min/max成为一个列表,使它看起来更"数学上纯粹":

x = restrict(val+7, [0, 42])
Run Code Online (Sandbox Code Playgroud)

  • 把它放在一个函数中是很好的(并建议,如果你做了很多),但我认为`min`和`max`比一堆条件更清晰.(我不知道`add`是什么 - 只说`clamp(val + 7,0,42)`.) (5认同)
  • @格伦梅纳德。不确定我是否同意 min 和 max 更干净。使用它们的全部意义在于能够在一行中填充更多内容,从而有效地降低代码的可读性。 (3认同)

Jen*_*ens 7

在我看来,这似乎更pythonic:

>>> def clip(val, min_, max_):
...     return min_ if val < min_ else max_ if val > max_ else val
Run Code Online (Sandbox Code Playgroud)

一些测试:

>>> clip(5, 2, 7)
5
>>> clip(1, 2, 7)
2
>>> clip(8, 2, 7)
7
Run Code Online (Sandbox Code Playgroud)


Gre*_*ill 6

如果您的代码看起来太笨重,那么函数可能会有所帮助:

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

new_index = clamp(0, new_index, len(mylist)-1)
Run Code Online (Sandbox Code Playgroud)