如何将数字限制(剪辑、限制)到某个范围?

问题描述 投票:0回答:10

我有以下代码:

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 行。这相当冗长,有点难看......我敢说,它非常un-pythonic

还有其他更简单、更紧凑的解决方案吗?(以及更多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))

请参阅用上限和下限替换列表值的Pythonic 方法(钳位、剪裁、阈值)?了解处理 Numpy 数组中的值的特定技术。

python clamp
10个回答
190
投票

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

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

134
投票
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

70
投票

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

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


44
投票

参见numpy.clip

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

30
投票

max()
min()
链接在一起是我见过的常见习惯用法。如果你觉得很难读,可以写一个辅助函数来封装操作:

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

30
投票

我心爱的可读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])

22
投票

这个对我来说似乎更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

16
投票

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

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

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

0
投票

避免为如此小的任务编写函数,除非你经常应用它们,因为它会让你的代码变得混乱。

对于个人价值观:

min(clamp_max, max(clamp_min, value))

对于值列表:

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

0
投票

很多好的答案,我只想添加一个打字的答案

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from _typeshed import SupportsAllComparisons

SupportsAllComparisonsT = TypeVar("SupportsAllComparisonsT", bound="SupportsAllComparisons")

def clip(
    val: SupportsAllComparisonsT, minval: SupportsAllComparisonsT, maxval: SupportsAllComparisonsT
) -> SupportsAllComparisonsT:
    """
    Limit a value to be within a certain range.

    Return the given `val` if it's between `minval` and `maxval`.
    Otherwise return `minval` or `maxval`.
    
    Also see `numpy.clip`
    """
    return minval if val < minval else maxval if val > maxval else val

© www.soinside.com 2019 - 2024. All rights reserved.