Как зафиксировать целое число в некотором диапазоне? - PullRequest
79 голосов
/ 04 ноября 2010

У меня есть следующий код:

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

Ответы [ 9 ]

100 голосов
/ 04 ноября 2010

Это довольно ясно, на самом деле.Многие люди учатся этому быстро.Вы можете использовать комментарий, чтобы помочь им.

new_index = max(0, min(new_index, len(mylist)-1))
72 голосов
/ 04 ноября 2010
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
36 голосов
/ 16 мая 2011

См. numpy.clip :

index = numpy.clip(index, 0, len(my_list) - 1)
34 голосов
/ 07 апреля 2014

много интересных ответов здесь, все примерно одинаково, кроме ... какой из них быстрее?

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 есть !, используйте обычный старый питон. Numpy версия, пожалуй, неудивительно, самая медленная из всех. Возможно, потому что он ищет массивы, где другие версии просто упорядочивают свои аргументы.

13 голосов
/ 04 ноября 2010

Цепочка max() и min() вместе - нормальная идиома, которую я видел.Если вам трудно читать, напишите вспомогательную функцию для инкапсуляции операции:

def clamp(minimum, x, maximum):
    return max(minimum, min(x, maximum))
13 голосов
/ 04 ноября 2010

Что случилось с моим любимым читаемым языком 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)

Если вы хотите, вы можете даже сделать min / max списком, чтобы он выглядел более «математически чистым»:

x = restrict(val+7, [0, 42])
5 голосов
/ 04 ноября 2010

Если ваш код кажется слишком громоздким, может помочь функция:

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

new_index = clamp(0, new_index, len(mylist)-1)
4 голосов
/ 02 мая 2016

Этот мне кажется более питоническим:

>>> 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
1 голос
/ 04 октября 2011

Избегайте написания функций для таких небольших задач, если вы не применяете их часто, так как это загромождает ваш код.

для индивидуальных значений:

min(clamp_max, max(clamp_min, value))

для списков значений:

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