Нарезка поведения 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). Так что это задокументировано, но мало кто когда-либо читал это.