Нарезка поведения Python Range() [:]
Я наткнулся на интересный фрагмент кода в обзоре контроля качества и был удивлен его поведением. Мне любопытно, если это где-нибудь задокументировано.
for i in range(0, my_array.max(), 3)[:]:
# other code here
Мне было интересно узнать о необходимости [:]
после range
, поэтому я проверил его:
>>> range(0, 10, 3)
range(0, 10, 3)
>>> range(0, 10, 3)[:]
range(0, 12, 3)
Фактическая последовательность, определяемая этими диапазонами, идентична, но я не вижу такого поведения срезов, документированного где-либо в документации диапазона Python, поэтому мне было любопытно, что на самом деле здесь происходит.
Ответы
Ответ 1
На мгновение представим, что range
все еще возвращает list
. Нарезка объекта range
возвращает объект range
, который будет действовать так, как если бы вы нарезали базовый список. Вместо того чтобы делать это со списком, объект range
может позаботиться об этом в постоянное время, используя арифметику.
>>> range(0, 90, 2)[10:23]
range(20, 46, 2)
>>> list(range(0, 90, 2)[10:23])
[20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44]
Когда вы делаете что-то вроде:
range(0, 10, 3)[:]
Питон нарезает его арифметикой.
Я предполагаю, что при определении конечного элемента он округляется. Он пытается вычислить нулевой элемент в диапазоне для начала. Это будет start + step * 0 = 0
.
Затем Python пытается получить конечный элемент. В диапазоне есть элементы (10 - 0) // 3 + 1 = 4
, поэтому конечным элементом является start + step * n_elements = 0 + 3 * 4 = 12
.
Ответ 2
Я думаю, что некоторые вещи смешаны здесь.
range
производит поведение среза, потому что имеет смысл иметь срезы с индексами не по умолчанию:
>>> list(range(10, 20)[3:7])
[13, 14, 15, 16]
- Существует идиома копирования списка (который является изменяемым) путем создания среза со всеми индексами по умолчанию:
some_list[:]
эквивалентен чему-то вроде [x for x in some_list]
.
- Существует странный код, который делает
[:]
для объекта диапазона (или фактического списка, если это Python 2), который не имеет смысла. Сгенерированный объект/список диапазона нигде больше не упоминается.
- Документация Python перечисляет разделение среди "общих операций с последовательностями" в главе "Типы последовательностей -
list
, tuple
, range
" (emph. Mine). Так что это задокументировано, но мало кто когда-либо читал это.