Почему фрагменты в 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?
Ответы
Ответ 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; представление потеряно.