Почему фрагменты в Python 3 по-прежнему являются копиями, а не представлениями? - PullRequest
47 голосов
/ 01 августа 2011

Как я только сейчас заметил после комментирования этого ответа , фрагменты в Python 3 возвращают мелкие копии того, что они нарезают, а не представления. Почему это все еще так? Даже оставляя в стороне использование представлений numpy, а не копий для нарезки, тот факт, что dict.keys, dict.values и dict.items все возвращают представления в Python 3, и что есть много других аспектов Python 3, ориентированных на более широкое использование Итераторы создают впечатление, что было бы движение к тому, чтобы срезы становились похожими. У itertools есть функция islice, которая создает итеративные срезы, но она более ограничена, чем обычные срезы, и не обеспечивает функциональные возможности просмотра вдоль линий dict.keys или dict.values.

Кроме того, тот факт, что вы можете использовать присваивание срезами для изменения исходного списка, но срезы сами являются копиями, а не представлениями, является противоречивым аспектом языка и, похоже, нарушает несколько принципов, проиллюстрированных в Дзен Питона .

То есть факт, что вы можете сделать

>>> a = [1, 2, 3, 4, 5]
>>> a[::2] = [0, 0, 0]
>>> a
[0, 2, 0, 4, 0]

но не

>>> a = [1, 2, 3, 4, 5]
>>> a[::2][0] = 0
>>> a
[0, 2, 3, 4, 5]

или что-то вроде

>>> a = [1, 2, 3, 4, 5]
>>> b = a[::2]
>>> b
view(a[::2] -> [1, 3, 5])   # numpy doesn't explicitly state that its slices are views, but it would probably be a good idea to do it in some way for regular Python
>>> b[0] = 0
>>> b
view(a[::2] -> [0, 3, 5])
>>> a
[0, 2, 3, 4, 5]

Кажется несколько произвольным / нежелательным.

Мне известно о http://www.python.org/dev/peps/pep-3099/ и той части, где написано «Срезы и расширенные срезы не исчезнут (даже если API-интерфейсы __getslice__ и __setslice__ могут быть заменены) и не вернутся. представления для стандартных типов объектов. ", но в связанном обсуждении не упоминается, почему было принято решение о разрезании с представлениями; на самом деле, большинство комментариев к этому конкретному предложению из предложений, перечисленных в оригинальном сообщении, казалось положительным.

Что помешало реализовать что-то подобное в Python 3.0, который был специально разработан, чтобы не быть строго обратно совместимым с Python 2.x и, таким образом, был бы лучшим временем для реализации такого изменения в дизайне, и есть ли что-нибудь, что может помешать этому в будущих версиях Python?

Ответы [ 2 ]

10 голосов
/ 01 августа 2011

Кроме того, тот факт, что вы можете использовать присваивание срезами для изменения исходного списка, но срезы сами являются копиями, а не представлениями.

Хм .. это не совсем верно; хотя я вижу, как ты мог так думать. На других языках назначение среза, что-то вроде:

a[b:c] = d

эквивалентно

tmp = a.operator[](slice(b, c)) # which returns some sort of reference
tmp.operator=(d)        # which has a special meaning for the reference type.

Но в python первое утверждение фактически преобразуется в следующее:

a.__setitem__(slice(b, c), d)

То есть назначение элемента в Python фактически специально распознается как имеющее особое значение, отдельное от поиска элемента и назначения ; они могут быть не связаны. Это согласуется с питоном в целом, потому что у питона нет таких понятий, как "lvalues" , найденные в C / C ++; Нет способа перегрузить сам оператор присваивания; только конкретные случаи, когда левая сторона присвоения не является простым идентификатором.

Предположим, списки сделали имеют представления; И вы пытались его использовать:

myView = myList[1:10]
yourList = [1, 2, 3, 4]
myView = yourList

В языках, кроме python, может быть способ вставить yourList в myList, но в python, поскольку имя myView отображается как пустой идентификатор, оно может означать только переменную assignemnt; вид потерян.

2 голосов
/ 04 августа 2011

Что ж, похоже, я нашел много аргументов в пользу решения views, исходя из потока, начинающегося с http://mail.python.org/pipermail/python-3000/2006-August/003224.html (речь идет главным образом о нарезке строк, но по крайней мере в одном письме в потоке упоминаются изменяемые объектыкак списки), а также некоторые вещи из:

http://mail.python.org/pipermail/python-3000/2007-February/005739.html
http://mail.python.org/pipermail/python-dev/2008-May/079692.html и последующих писем в ветке

Похоже, преимущества перехода на этотстиль для базового Python был бы значительно перевешен вызванной сложностью и различными нежелательными крайними случаями.Да ладно.

... И когда я начал интересоваться возможностью простой замены текущего способа работы slice объектов с повторяющейся формой а-ля itertools.islice, такой же как zip,map и т. Д. Все возвращают итерируемые элементы вместо списков в Python 3, я начал осознавать все неожиданное поведение и возможные проблемы, которые могут возникнуть из этого.Похоже, на данный момент это может быть тупик.

С другой стороны, массивы numpy достаточно гибкие, поэтому в ситуациях, когда такие вещи могут быть необходимы, использовать их будет не сложно.ndarrays вместо списков.Тем не менее, кажется, что ndarrays не поддерживает использование нарезки для вставки дополнительных элементов в массивы, как это происходит со списками Python:

>>> a = [0, 0]
>>> a[:1] = [2, 3]
>>> a
[2, 3, 0]

Я думаю, что вместо этого эквивалент numpy будет выглядеть примерно так:*

Несколько более сложный случай:

>>> a = [1, 2, 3, 4]
>>> a[1:3] = [0, 0, 0]
>>> a
[1, 0, 0, 0, 4]

против

>>> a = np.array([1, 2, 3, 4])
>>> a = np.hstack((a[:1], [0, 0, 0], a[3:]))
>>> a
array([1, 0, 0, 0, 4])

И, конечно, приведенные выше примеры не сохраняют результат в исходном массиве, как это происходитс обычным расширением списка Python.

...