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


92

我有以下代码:

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]

基本上,我计算一个新索引并使用它从列表中查找一些元素。为了确保索引在列表的范围内,我需要将这2条if语句写成4行。这很冗长,有点丑陋……我敢说,这很不合蟒蛇性

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

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

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

我也知道我可以链max()min()在一起。它更紧凑,但是我觉得它有点晦涩难懂,如果我输入错误的话,更难发现错误。换句话说,我觉得它不是很简单。

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

2
如果感觉“有点模糊”,可以使用它吗?
圣诞老人

1
是的,我可以编写一个函数,但这不是重点。问题是如何实现它(内联或在函数中)。
DenilsonSáMaia,2010年

clamp = lambda value, minv, maxv: max(min(value, maxv), minv)使用arma.sourceforge.net/docs.html#clamp
Dima Tisnek

Answers:


119

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

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

12
尽管我觉得它不像它应该的那样Python化,但我也觉得这是我们现在拥有的最好的解决方案。
DenilsonSáMaia 2010年

49
def clamp(n, smallest, largest): return max(smallest, min(n, largest))
csl 2015年

3
@csl人们总是提供这些小的辅助函数,但我不知道将它们放在哪里。helperFunctions.py?一个单独的模块?如果它因各种完全不同的事情而散布着各种“辅助功能”怎么办?
Mateen Ulhaq '17

1
我不知道,但是如果您收集了很多内容并将它们分类为明智的模块,为什么不放在GitHub上并从中创建一个PyPi包呢?可能会变得流行。
csl

@MateenUlhaqutils.py
Wouterr

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

例如:

>>> 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

10
+1用于sorted()内置广告素材。非常紧凑,但是有点晦涩。无论如何,很高兴看到其他创造性的解决方案!
DenilsonSáMaia

10
非常有创造力,实际上与min(max())建造速度差不多。如果该数字在此范围内且不需要交换,则速度会稍微快一点。
kindall 2010年

40

这里有许多有趣的答案,几乎都是一样的,除了...哪个更快?

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拥有它!,请使用普通ol'python。numpy版本是最慢的版本,也许并不奇怪。可能是因为它正在寻找数组,而其他版本只是在其中对它们的参数进行排序。


7
@LenarHoyt并不奇怪,考虑到Numpy的性能是围绕大型数组而不是单个数字设计的。同样,它必须首先将整数转换为内部数据类型,并且由于它接受几种不同类型的输入,因此可能需要花费大量时间才能确定输入是什么类型以及将其转换为什么类型。如果为它提供包含数千个值的数组(最好不是列表或元组,首先要转换该数组或元组),则Numpy的性能会更好。
blubberdiblub

Python慢​​了三个数量级。783 ns = 783,000 µs。我过去犯过同样的错误。表示法很微妙。
达斯汀·安德鲁斯

5
@DustinAndrews你已经倒退了。1 µs是10 ^ -6秒,1 ns是10 ^ -9秒。python示例在0.784 µs内完成了1个循环。至少,它是在我测试过的机器上完成的。这个微基准与其他微基准一样有用。它可以使您摆脱真正的坏主意,但可能不会帮助您找到实际上最快的编写有用代码的方法。
SingleNegationElimination

函数调用有一些开销。我没有做过的基准,但它很可能是mm_clippy_clip将同样快,如果你使用JIT编译器,像PyPy。除了前者更具可读性,而且可读性在大多数情况下比轻微的性能提升在Python的哲学中更为重要。
Highstaker

@DustinAndrews我建议删除您的事实不正确的评论,因为您确实将它弄反了。
Acumenus

38

参见numpy.clip

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

医生说的第一个参数clipa,“一个包含要裁剪的元素的数组”。所以,你会写numpy.clip([index], …,不是numpy.clip(index, …
罗里·奥肯

13
@ RoryO'Kane:您尝试过吗?
Neil G

1
熊猫还允许在“系列”和“数据框”以及“面板”上使用此功能。
Nour Wolf


14

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

认真地说,只要使其具有功能即可:

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

然后只需使用类似这样的名称即可:

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

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

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

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

如果愿意,您甚至可以将最小/最大列表作为一个列表,这样看起来就更“纯数学”:

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

6
将其放在函数中很好(并且建议您进行很多操作),但是我认为min并且max比一堆条件语句更清楚。(我不知道这add是什么,只是说clamp(val + 7, 0, 42)。)
格伦·梅纳德

1
@GlennMaynard。不知道我是否同意最小和最大更干净。使用它们的全部目的是能够将更多内容填充到一行上,从而有效地减少了代码的可读性。
疯狂物理学家

9

在我看来,这似乎更pythonic:

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

一些测试:

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

7

如果您的代码看起来太笨拙,则一个函数可能会有所帮助:

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

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

2

避免编写用于此类小任务的函数,除非您经常应用它们,否则会导致代码混乱。

对于单个值:

min(clamp_max, max(clamp_min, value))

对于值列表:

map(lambda x: min(clamp_max, max(clamp_min, x)), values)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.