Я работаю в области, в которой диапазоны обычно описываются включительно. У меня есть понятные человеку описания, такие как from A to B
, которые представляют диапазоны, которые включают обе конечные точки - например, from 2 to 4
означает 2, 3, 4
.
Каков наилучший способ работы с этими диапазонами в коде Python? Следующий код работает для генерации инклюзивных диапазонов целых чисел, но мне также необходимо выполнить инклюзивные операции срезов:
def inclusive_range(start, stop, step):
return range(start, (stop + 1) if step >= 0 else (stop - 1), step)
Единственное полное решение, которое я вижу, - это явное использование + 1
(или - 1
) каждый раз, когда я использую range
или обозначение среза (например, range(A, B + 1)
, l[A:B+1]
, range(B, A - 1, -1)
). Это повторение действительно лучший способ работать с инклюзивными диапазонами?
Изменить: Спасибо L3viathan за ответ. Написание функции inclusive_slice
для дополнения inclusive_range
, безусловно, вариант, хотя я, вероятно, написал бы ее следующим образом:
def inclusive_slice(start, stop, step):
...
return slice(start, (stop + 1) if step >= 0 else (stop - 1), step)
...
здесь представляет код для обработки отрицательных индексов, которые непросты при использовании со слайсами - обратите внимание, например, что функция L3viathan дает неверные результаты, если slice_to == -1
.
Однако кажется, что использовать функцию inclusive_slice
было бы неудобно - действительно ли l[inclusive_slice(A, B)]
лучше, чем l[A:B+1]
?
Есть ли лучший способ обработки инклюзивных диапазонов?
Изменить 2: Спасибо за новые ответы. Я согласен с Фрэнсисом и Корли, что изменение значения операций срезов, глобально или для определенных классов, приведет к значительной путанице. Поэтому я сейчас склоняюсь к написанию функции inclusive_slice
.
Чтобы ответить на мой собственный вопрос из предыдущего редактирования, я пришел к выводу, что использование такой функции (например, l[inclusive_slice(A, B)]
) было бы лучше, чем ручное добавление/вычитание 1 (например, l[A:B+1]
), поскольку это позволило бы получить крайние случаи ( такие как B == -1
и B == None
) для обработки в одном месте. Можем ли мы уменьшить неловкость при использовании функции?
Изменить 3: Я думал о том, как улучшить синтаксис использования, который в настоящее время выглядит как l[inclusive_slice(1, 5, 2)]
. В частности, было бы хорошо, если бы создание включающего слайса напоминало стандартный синтаксис слайса. Для этого вместо inclusive_slice(start, stop, step)
может быть функция inclusive
, которая принимает срез в качестве параметра. Идеальным синтаксисом использования для inclusive
была бы строка 1
:
l[inclusive(1:5:2)] # 1
l[inclusive(slice(1, 5, 2))] # 2
l[inclusive(s_[1:5:2])] # 3
l[inclusive[1:5:2]] # 4
l[1:inclusive(5):2] # 5
К сожалению, это не разрешено Python, который позволяет использовать только синтаксис :
в []
. Поэтому inclusive
должен вызываться с использованием синтаксиса 2
или 3
(где s_
действует как версия, предоставленная numpy).
Другие возможности состоят в том, чтобы превратить inclusive
в объект с __getitem__
, разрешающим синтаксис 4
, или применить inclusive
только к параметру stop
слайса, как в синтаксисе 5
. К сожалению, я не верю, что последнее можно заставить работать, поскольку inclusive
требует знания значения step
.
Из возможных синтаксисов (оригинал l[inclusive_slice(1, 5, 2)]
, плюс 2
, 3
и 4
), какой из них лучше всего использовать? Или есть другой, лучший вариант?
Окончательное редактирование: Спасибо всем за ответы и комментарии, это было очень интересно. Я всегда был поклонником философии Python "один способ сделать это", но эта проблема была вызвана конфликтом между Python "один путь" и "один путь", запрещенным проблемной областью. Я определенно оценил TIMTOWTDI в языковом дизайне.
За то, что дал первый и получивший наибольшее количество голосов ответ, я присуждаю награду L3viathan.