정수를 특정 범위로 고정하는 방법은 무엇입니까?


94

다음 코드가 있습니다.

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]

기본적으로 새 인덱스를 계산하고이를 사용하여 목록에서 일부 요소를 찾습니다. 인덱스가 목록의 범위 내에 있는지 확인하기 위해 두 개의 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
예, 함수를 작성할 수 있지만 그게 요점이 아닙니다. 문제는이를 구현하는 방법 (인라인 또는 함수)입니다.
Denilson Sá Maia

clamp = lambda value, minv, maxv: max(min(value, maxv), minv)에서 API를 사용 arma.sourceforge.net/docs.html#clamp
디마 Tisnek

답변:


120

이것은 사실 꽤 분명합니다. 많은 사람들이 그것을 빨리 배웁니다. 댓글을 사용하여 도움을 줄 수 있습니다.

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

12
그래야만 비단뱀처럼 느껴지지는 않지만 이것이 현재 우리가 가진 최고의 솔루션이라고 생각합니다.
Denilson Sá Maia

50
def clamp(n, smallest, largest): return max(smallest, min(n, largest))
csl

3
@csl Folks는 항상 이러한 작은 도우미 기능을 제공하지만 어디에 두어야할지 모르겠습니다. helperFunctions.py? 별도의 모듈? 이것이 완전히 다른 것들을위한 다양한 "도우미 기능"으로 흩어져 있다면 어떨까요?
Mateen Ulhaq

2
몰라요.하지만 많은 것을 모아서 합리적인 모듈로 분류한다면 왜 GitHub에 올려 놓고 그것으로 PyPi 패키지를 만드는 건 어떨까요? 아마도 인기가있을 것입니다.
csl

@MateenUlhaqutils.py
Wouterr

90
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

12
sorted()내장의 창의적인 사용을위한 +1 . 매우 작지만 약간 모호합니다. 어쨌든 다른 창의적인 솔루션을 보는 것은 항상 좋습니다!
Denilson Sá Maia

12
매우 창의적이고 실제로 min(max())건설 만큼 빠릅니다 . 숫자가 범위 내에 있고 스왑이 필요하지 않은 경우 매우 약간 더 빠릅니다.
kindall 2010

41

여기에 많은 흥미로운 답변이 있습니다. 모두 비슷하지만 ... 어느 것이 더 빠릅니까?

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

파이썬은 3 배 더 느립니다. 783ns = 783,000µs. 나는 과거에 같은 실수를 저질렀다. 표기법은 미묘합니다.
Dustin Andrews

7
@DustinAndrews 당신은 거꾸로 가지고 있습니다. 1µs는 10 ^ -6 초, 1ns는 10 ^ -9 초입니다. 파이썬 예제는 0.784 µs에서 1 개의 루프를 완료합니다. 또는 적어도 내가 테스트 한 컴퓨터에서 수행되었습니다. 이 마이크로 벤치 마크는 다른 마이크로 벤치 마크만큼 유용합니다. 정말 나쁜 아이디어에서 멀어 질 수는 있지만 유용한 코드 를 작성 하는 가장 빠른 방법을 찾는 데는 도움이되지 않을 것입니다 .
SingleNegationElimination

함수 호출에 약간의 오버 헤드가 있습니다. 나는 벤치 마크를 수행하지 않은, 그러나 것이 확실히 가능 mm_clip하고 py_clip당신이 PyPy처럼, JIT 컴파일러를 사용하는 경우 동일하게 빠른 것입니다. 전자가 더 읽기 쉽고 가독성이 대부분의 경우 약간의 성능 향상보다 Python의 철학에서 더 중요합니다.
Highstaker

@DustinAndrews 거꾸로 가져 왔기 때문에 사실적으로 잘못된 댓글을 삭제하는 것이 좋습니다.
Acumenus

38

numpy.clip 참조 :

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

워드 프로세서는 첫 번째 파라미터는 말 clip이고 a,에 "요소를 포함하는 배열 클립". 그래서 당신은 작성해야 numpy.clip([index], …하지 numpy.clip(index, ….
Rory O'Kane 2013-08-27

13
@ RoryO'Kane : 시도 했습니까?
Neil G

1
Pandas는 시리즈와 데이터 프레임, 패널에서도이를 허용합니다.
Nour Wolf

17

Chaining max()and min()together는 내가 본 정상적인 관용구입니다. 읽기 어려운 경우 작업을 캡슐화하는 도우미 함수를 작성하십시오.

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

14

내가 사랑하는 읽을 수있는 파이썬 언어는 어떻게 되었습니까? :-)

진지하게, 그냥 함수로 만드세요 :

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
함수에서 그것을 두는 것은 괜찮습니다 (당신이 그것을에게 많은 일을하는 경우, 권고),하지만 난 생각 minmax조건문의 무리보다 훨씬 명확하다. (저는 무엇 add을위한 것인지 모르겠습니다 clamp(val + 7, 0, 42). 그냥 말 하세요.)
Glenn Maynard

1
@GlennMaynard. 최소값과 최대 값이 더 깨끗하다는 데 동의 할 수 있는지 모르겠습니다. 그것들을 사용하는 요점은 한 줄에 더 많은 내용을 담아 효과적으로 코드를 읽기 어렵게 만드는 것입니다.
Mad Physicist

10

이것은 나에게 더 비단뱀처럼 보입니다.

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

8

코드가 너무 복잡해 보이면 함수가 도움이 될 수 있습니다.

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)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.