Làm thế nào để kẹp một số nguyên vào một số phạm vi?


92

Tôi có mã sau:

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]

Về cơ bản, tôi tính toán một chỉ mục mới và sử dụng chỉ mục đó để tìm một số phần tử từ danh sách. Để đảm bảo chỉ mục nằm trong giới hạn của danh sách, tôi cần viết 2 ifcâu lệnh đó thành 4 dòng. Điều đó khá dài dòng, một chút xấu xí ... Tôi dám nói, nó khá là không quan trọng .

Có giải pháp nào khác đơn giản và gọn nhẹ hơn không? (và nhiều pythonic hơn )

Có, tôi biết tôi có thể sử dụng if elsetrong một dòng, nhưng không thể đọc được:

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

Tôi cũng biết tôi có thể xâu chuỗi max()min()cùng nhau. Nó nhỏ gọn hơn, nhưng tôi cảm thấy nó hơi tối nghĩa, khó tìm lỗi hơn nếu tôi gõ sai. Nói cách khác, tôi thấy nó không đơn giản lắm.

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

2
Nếu cảm thấy "hơi tối nghĩa", hãy tạo một chức năng khỏi nó?
Ông già Noel

1
Vâng, tôi có thể viết một hàm, nhưng đó không phải là vấn đề. Câu hỏi đặt ra là làm thế nào để thực hiện điều đó (nội tuyến hoặc trong một hàm).
Denilson Sá Maia

clamp = lambda value, minv, maxv: max(min(value, maxv), minv)Sử dụng API từ arma.sourceforge.net/docs.html#clamp
Dima Tisnek

Câu trả lời:


119

Điều này khá rõ ràng, thực sự. Nhiều người học nó một cách nhanh chóng. Bạn có thể sử dụng một bình luận để giúp họ.

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

12
Mặc dù tôi cảm thấy nó không phải là vấn đề đáng lo ngại, nhưng tôi cũng cảm thấy đây là giải pháp tốt nhất mà chúng tôi có bây giờ.
Denilson Sá Maia

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

3
@csl Folks luôn cung cấp các chức năng trợ giúp nhỏ này, nhưng tôi không bao giờ biết phải đặt chúng ở đâu. helperFunctions.py? Một mô-đun riêng biệt? Điều gì sẽ xảy ra nếu điều này được rải rác với nhiều "chức năng trợ giúp" cho những thứ hoàn toàn khác nhau?
Mateen Ulhaq

1
Tôi không biết, nhưng nếu bạn thu thập nhiều thứ đó và phân loại chúng thành các mô-đun hợp lý, tại sao không đặt trên GitHub và tạo một gói PyPi từ nó? Có thể sẽ trở nên phổ biến.
csl

@MateenUlhaqutils.py
Wouterr

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

ví dụ:

>>> 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 để sử dụng sáng tạo các sorted()cài sẵn. Rất nhỏ gọn, nhưng nó chỉ là một chút tối nghĩa. Dù sao, thật tuyệt khi thấy các giải pháp sáng tạo khác!
Denilson Sá Maia

10
Rất sáng tạo và thực sự nhanh như việc min(max())xây dựng. Nhanh hơn một chút trong trường hợp số nằm trong phạm vi và không cần hoán đổi.
kindall

40

nhiều câu trả lời thú vị ở đây, tất cả đều giống nhau, ngoại trừ ... câu trả lời nào nhanh hơn?

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 có nó !, hãy sử dụng python đơn giản. Có lẽ không có gì ngạc nhiên khi phiên bản numpy là phiên bản chậm nhất trong số các phiên bản. Có lẽ vì nó đang tìm kiếm mảng, nơi các phiên bản khác chỉ sắp xếp các đối số của chúng.


7
@LenarHoyt không có gì đáng ngạc nhiên khi xem xét rằng hiệu suất của Numpy được thiết kế xung quanh các mảng lớn chứ không phải các số đơn lẻ. Ngoài ra, nó phải chuyển đổi số nguyên thành một kiểu dữ liệu bên trong trước tiên và vì nó chấp nhận một số loại đầu vào khác nhau, nên có thể mất thời gian đáng kể để tìm ra loại đầu vào là gì và chuyển đổi nó thành cái gì. Bạn sẽ thấy hiệu suất Numpy tốt hơn nhiều nếu bạn cung cấp cho nó một mảng (tốt hơn là không phải là một danh sách hoặc bộ mã, mà nó phải chuyển đổi trước) gồm vài nghìn giá trị.
blubberdiblub

Python chậm hơn ba bậc độ lớn. 783 ns = 783.000 µs. Tôi đã từng mắc sai lầm tương tự trong quá khứ. Ký hiệu là tinh tế.
Dustin Andrews

5
@DustinAndrews bạn đã hiểu ngược lại. 1 µs là 10 ^ -6 giây, 1 ns là 10 ^ -9 giây. ví dụ python hoàn thành 1 vòng lặp trong 0,784 µs. Hoặc ít nhất, nó đã xảy ra trên chiếc máy tôi đã thử nghiệm. Microbenchmark này hữu ích như bất kỳ microbenchmark nào khác; nó có thể giúp bạn tránh xa những ý tưởng thực sự tồi tệ nhưng có lẽ sẽ không giúp bạn nhiều tìm ra cách thực sự nhanh nhất để viết mã hữu ích .
SingleNegationElimination

Có một chút chi phí trên cuộc gọi của các chức năng. Tôi chưa thực hiện các điểm chuẩn, nhưng hoàn toàn có thể xảy ra mm_clippy_clipsẽ nhanh không kém nếu bạn sử dụng trình biên dịch JIT, như PyPy. Ngoại trừ cái trước dễ đọc hơn, và khả năng đọc được quan trọng hơn trong triết lý của Python hơn là phần lớn thời gian đạt được một chút hiệu suất.
Highstaker

@DustinAndrews Tôi khuyên bạn nên xóa nhận xét thực tế không chính xác của bạn vì bạn đã làm nó bị ngược.
Acumenus

38

Xem numpy.clip :

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

Các tài liệu cho biết tham số đầu tiên của clipa, một "mảng chứa các phần tử để cắt". Vì vậy, bạn sẽ phải viết numpy.clip([index], …, không phải numpy.clip(index, ….
Rory O'Kane

13
@ RoryO'Kane: Bạn đã thử chưa?
Neil G

1
Pandas cũng cho phép điều này trên Series và DataFrames và Panels.
Nour Wolf,

17

Chuỗi max()min()cùng nhau là câu thành ngữ bình thường mà tôi từng thấy. Nếu bạn cảm thấy khó đọc, hãy viết một hàm trợ giúp để đóng gói hoạt động:

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

14

Điều gì đã xảy ra với ngôn ngữ Python có thể đọc được yêu quý của tôi? :-)

Nghiêm túc mà nói, chỉ cần biến nó thành một chức năng:

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

sau đó chỉ cần gọi nó với một cái gì đó như:

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

Hoặc một giải pháp đơn giản hơn, linh hoạt hơn, nơi bạn tự tính toán:

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

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

Nếu muốn, bạn thậm chí có thể tạo danh sách tối thiểu / tối đa để danh sách trông "thuần túy về mặt toán học" hơn:

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

6
Đưa nó vào một hàm là tốt (và được khuyên, nếu bạn đang làm nó nhiều), nhưng tôi nghĩ minmaxrõ ràng hơn nhiều so với một loạt các điều kiện. (Tôi không biết dùng để làm gì add- chỉ cần nói clamp(val + 7, 0, 42).)
Glenn Maynard

1
@GlennMaynard. Không chắc rằng tôi có thể đồng ý rằng min và max là sạch hơn. Toàn bộ điểm của việc sử dụng chúng là có thể nhồi nhiều hơn vào một dòng, làm cho mã ít dễ đọc hơn.
Mad Physicist:

10

Cái này đối với tôi có vẻ khó hiểu hơn:

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

Một vài thử nghiệm:

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

8

Nếu mã của bạn có vẻ quá khó sử dụng, một hàm có thể giúp:

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

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

2

Tránh viết các hàm cho các tác vụ nhỏ như vậy, trừ khi bạn áp dụng chúng thường xuyên, vì nó sẽ làm rối mã của bạn.

cho các giá trị riêng lẻ:

min(clamp_max, max(clamp_min, value))

cho danh sách các giá trị:

map(lambda x: min(clamp_max, max(clamp_min, x)), values)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.