Как назначение работает со срезом списка Python?

Python doc говорит, что нарезка списка возвращает новый список.
Теперь, если возвращается "новый" список, у меня есть следующие сомнения, связанные с "Назначением на кусочки"

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

Теперь результат будет:

[4, 5, 3] 
  1. Как что-то, что возвращает что-то, может появиться на левой стороне выражения?
  2. Да, я прочитал документы, и там написано, что это возможно. Теперь, когда нарезка списка возвращает "новый" список, почему изменяется первоначальный список? Я не могу понять механику, стоящую за этим.

Ответ 1

Вы смешиваете две разные операции, которые используют очень похожий синтаксис:

1) нарезка:

b = a[0:2]

Это делает копию среза a и назначает его b.

2) назначение среза:

a[0:2] = b

Это заменяет срез a содержимым b.

Хотя синтаксис подобен (я полагаю, по дизайну!), это две разные операции.

Ответ 2

При указании на левой стороне a = оператора, вы используете Python нормальное назначение, которое изменяет имя a, в данном контексте, чтобы указать новое значение. Это не меняет предыдущее значение, на которое указывал a.

Указывая a[0:2] в левой части оператора =, вы сообщаете Python, что хотите использовать Slice Assignment. Назначение фрагментов - это специальный синтаксис для списков, в который можно вставлять, удалять или заменять содержимое из списка:

Вставка:

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

Удаление:

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

Замена:

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

Замечания:

Длина среза может отличаться от длины назначенной последовательности, тем самым изменяя длину целевой последовательности, если целевая последовательность это позволяет. - источник

Назначение фрагментов обеспечивает функцию, аналогичную распаковке кортежей. Например, a[0:1] = [4, 5] эквивалентно:

# Tuple Unpacking
a[0], a[1] = [4, 5]

С помощью Tuple Unpacking вы можете изменять непоследовательные списки:

>>> a
[4, 5, 3]
>>> a[-1], a[0] = [7, 3]
>>> a
[3, 5, 7]

Однако распаковка кортежа ограничена заменой, поскольку вы не можете вставлять или удалять элементы.

До и после всех этих операций, a и тот же точный список. Python просто предоставляет хороший синтаксический сахар для изменения списка на месте.

Ответ 3

Я сталкивался с тем же вопросом раньше, и он касался языковой спецификации. Согласно заявлениям- уступкам,

  1. Если левой стороной назначения является подписка, Python вызовет __setitem__ для этого объекта. a[i] = x эквивалентно a.__setitem__(i, x).

  2. Если левой стороной присваивания является слайс, Python также вызовет __setitem__, но с другими аргументами: a[1:4]=[1,2,3] эквивалентно a.__setitem__(slice(1,4,None), [1,2,3])

Поэтому срез списка в левой части '=' ведет себя по-разному.

Ответ 4

Нарезая левую часть операции присваивания, вы указываете, какие элементы назначать.