Почему Python вставляет None в шаги слайса? - PullRequest
5 голосов
/ 09 декабря 2011

Это лучше всего проиллюстрировать на примере (все примеры предполагают, что ast импортировано; обратите внимание, что я использую Python 2.7.1):

# Outputs: Slice(lower=Num(n=1), upper=Num(n=10), step=None)
ast.dump(ast.parse("l[1:10]").body[0].value.slice)

# Outputs: Slice(lower=Num(n=1), upper=Num(n=10), step=Name(id='None', ctx=Load()))
ast.dump(ast.parse("l[1:10:]").body[0].value.slice)

# Outputs: Slice(lower=Num(n=1), upper=None, step=None)
ast.dump(ast.parse("l[1:]").body[0].value.slice)

# Outputs: Slice(lower=None, upper=None, step=None)
ast.dump(ast.parse("l[:]").body[0].value.slice)

Итак, как мы видим, l[1:10]в результате получается узел AST, у которого есть два дочерних элемента - lower и upper, оба из которых имеют числовые литералы, - и пустой третий дочерний элемент step.Но [1:10:], который мы могли бы считать идентичным, устанавливает дочерний элемент step его среза в буквальное выражение None (Name(id='None', ctx=Load())).

Хорошо, подумал я.Возможно, Python рассматривает l[1:10:] и l[1:10] как совершенно разные виды выражений.Ссылка на выражение Python ( link ) определенно указывает на это;l[1:10] - это простое срезание, но l[1:10:] - это расширенное срезание (только с одним элементом среза).

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

# Outputs: Slice(lower=Num(n=1), upper=None, step=Name(id='None', ctx=Load()))
ast.dump(ast.parse("l[1::]").body[0].value.slice)

# Outputs: Slice(lower=None, upper=Num(n=10), step=Name(id='None', ctx=Load()))
ast.dump(ast.parse("l[:10:]").body[0].value.slice)

Более того, при дальнейшей проверке AST даже не рассматривает эти срезы какрасширенные нарезки.Вот как на самом деле выглядят расширенные срезы:

# Outputs: ExtSlice(dims=[Slice(lower=None, upper=None, step=Name(id='None', ctx=Load())), Slice(lower=None, upper=None, step=Name(id='None', ctx=Load()))])
ast.dump(ast.parse("l[::, ::]").body[0].value.slice)

Итак, вот мой вывод: AST всегда обрабатывает параметр step специально по какой-то причине, и, независимо от этого, узел Slice AST представляет длинный срез(Я полагаю, что не должно быть двух разных базовых Slice классов - ShortSlice и LongSlice - хотя я думаю, что это будет предпочтительным), и поэтому расширенный срез из одного элемента может быть представлен как обычныйSlice узел и делается по какой-то причине.Мне просто кажется неправильным интерпретировать параметры None как значения по умолчанию, но я понимаю, что это было целенаправленное проектное решение;буквенная вставка None и обработка длинных срезов как узлов Slice просто кажутся случайностями (или артефактами старых конструкций).

У кого-нибудь есть более информированное объяснение?

1 Ответ

3 голосов
/ 09 декабря 2011

Без такой обработки в расширенной записи слайса вы не сможете различить l[1:] и l[1::], и вы не сможете вызвать другие специальные методы - __getslice__ можно вызвать для обычного слайса, но __getitem__ должен вызываться для расширенного среза.

Так что это в основном вещь обратной совместимости для Python 2.x, которая исчезла в Python 3.x:

Python 3.2 (r32:88445, Mar 25 2011, 19:28:28) 
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import ast
>>> ast.dump(ast.parse("l[1:]").body[0].value.slice)
'Slice(lower=Num(n=1), upper=None, step=None)'
>>> ast.dump(ast.parse("l[1::]").body[0].value.slice)
'Slice(lower=Num(n=1), upper=None, step=None)'
>>> 

См. источник python2.7 для ast.c и описание модели данных для получения дополнительной информации.

...