Какой самый питонный способ проверить, к какому конкретному диапазону (из многих) принадлежит число? - PullRequest
1 голос
/ 31 октября 2019

Я знаю, что могу проверить, принадлежит ли число X определенному диапазону (из Y последовательных диапазонов), используя объединенный if a<X<b elif b<X<C else ..., но есть ли более лаконичный, питонный способ сделать это?

Ответы [ 4 ]

2 голосов
/ 31 октября 2019

Используйте any:

x = 57
ranges = [range(10), range(15,27), range(38,42), range(49, 63), range(70, 95)]
if any(x in range for range in ranges):
    ...

Если вы не можете использовать range (потому что вы проверяете интервал с нецелыми конечными точками), сохраните интервал как кортеж.

ranges = [(1.5, 3.7), ...]
if any(t1 < x < t2 for t1, t2 in ranges):
1 голос
/ 31 октября 2019

Есть несколько способов, этот кажется не таким уж плохим:

def in_range(number : int, arange : range) -> bool:
    return number in arange

ranges = [range(6), range(4, 14), range(10, 20), range(12, 34)]
results = list(filter(lambda arange : in_range(5, arange), ranges))
print(results)

[диапазон (0, 6), диапазон (4, 14)]

Вы можете упростить это, как это.

def in_range(number : int, ranges : List[range]) -> List[range]:
    return [arange for arange in ranges if number in arange]

ranges = [range(6), range(4, 14), range(10, 20), range(12, 34)]
print(in_range(6, ranges))

[диапазон (0, 6), диапазон (4, 14)]

Вы можете узнать это на лету.

def in_range(number : int, start : int, stop : int) -> bool:
    return number in range(start,stop)

Редактировать 1

Вы также можете использовать any или хотите проверить его на наличие float, в этом случае посмотрите @ chepner комментарий.

0 голосов
/ 31 октября 2019

Если вы хотите реализовать эквивалент оператора switch (в Python его нет), вы можете определить служебную функцию, чтобы сделать код более кратким и читабельным (т.е. избегать повторений):

def switch(value):
    def inRange(a,b): 
        return a <= value and value < b
    return inRange

number = 25

case = switch(number)
if   case(10,20): print("small")
elif case(20,50): print("medium")
elif case(50,90): print("large")
else:             print("huge")  

Вы также можете использовать это с троичными операторами:

inRange = switch(number)
size    = "small"  if inRange(10,20) else \
          "medium" if inRange(20,50) else \
          "large"  if inRange(50,90) else \
          "huge"

print(size)

Если ваши диапазоны являются последовательными, вы можете избежать повторения верхних границ, используя разделительный поиск (что даст лучшую производительность для больших списков диапазонов):

ranges = [10,20,50,90]
sizes  = ["small","medium","large","huge"]
from bisect import bisect_right
size = sizes[bisect_right(ranges,number)-1]
print(size)
0 голосов
/ 31 октября 2019

Если ваши последовательные диапазоны плотные (то есть между ними нет пробелов), я бы сделал

def get_range(boundaries, x):
    for a, b in zip(boundaries, boundaries[1:]):
        if a < x < b:
            return a, b

# Test example
boundaries = [0, 5, 10, 15, 20]
x = 13
a, b = get_range(boundaries, x)
print(a, b)  # 10 15

Обратите внимание, что точные границы не включены ни в один из диапазонов! Т.е. get_range(boundaries, 10) не находит ни одного диапазона. Чтобы это исправить, замените a < x < b на, например, a <= x < b.

. Это замечательно, если у вас много диапазонов, где жестко закодированная цепь if - elif будет непрактичной. Тем не менее, если у вас есть миллионы диапазонов, вы, возможно, захотите быть немного умнее, например, реализовать бинарный поиск.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...