Подтвердить что ты не робот

Почему фрагменты в Python 3 все еще копируются, а не просмотры?

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

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

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

>>> 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?

4b9b3361

Ответ 1

Ну, похоже, я нашел много аргументов в пользу решения мнений, идущих по потоку, начиная с 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, объекты обрабатываются с помощью итеративной формы a la itertools.islice, как и zip, map и т.д. все возвращают iterables вместо списков в Python 3, я начал осознавать все неожиданное поведение и возможные проблемы, которые могут возникнуть из этого. Похоже, теперь это может быть тупиком.

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

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

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

>>> a = np.array([0, 0])  # or a = np.zeros([2]), but that not important here
>>> a = np.hstack(([2, 3], a[1:]))
>>> a
array([2, 3, 0])

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

>>> 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])

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

Ответ 2

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

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

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, чтобы иметь особое значение, отдельно от поиска и назначения элемента; они могут быть несвязанными. Это согласуется с python в целом, поскольку у python нет таких понятий, как "lvalues", найденные в C/С++; Невозможно перегрузить сам оператор присваивания; только конкретные случаи, когда левая сторона задания не является простым идентификатором.

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

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

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