Обновление списка
Мне показалось, что я понял операции нарезания Python, но когда я попытался обновить список, я смутился:
>>> foo = [1, 2, 3, 4]
>>> foo[:1] = ['one'] # OK, foo updated
>>> foo
['one', 2, 3, 4]
>>> foo[:][1] = 'two' # why foo not updated?
>>> foo
['one', 2, 3, 4]
>>> foo[:][2:] = ['three', 'four'] # Again, foo not updated
>>> foo
['one', 2, 3, 4]
Почему обновление не обновляется после foo[:][1] = 'two'
?
Обновление: Возможно, я не ясно объяснил свои вопросы. Я знаю, что при разрезании создается новый список. Я сомневаюсь, почему назначение разреза обновляет список (например, foo[:1] = ['one']
), но если есть два уровня среза, он не обновляет исходный список (например, foo[:][2:] = ['three', 'four']
).
Ответы
Ответ 1
Это связано с тем, что python не имеет l-значения, которые могут быть назначены. Вместо этого некоторые выражения имеют форму присваивания, которая отличается.
A foo[something]
- синтаксический сахар для:
foo.__getitem__(something)
но a foo[something] = bar
является синтаксическим сахаром для довольно разных:
foo.__setitem__(something, bar)
Если срез представляет собой только частный случай something
, так что foo[x:y]
расширяется до
foo.__getitem__(slice(x, y, None))
и foo[x:y] = bar
расширяется до
foo.__setitem__(slice(x, y, None), bar)
Теперь __getitem__
с срезом возвращает новый список, который является копией указанного диапазона, поэтому его изменение не влияет на исходный массив. И назначение работ в силу __setitem__
является другим методом, который может просто сделать что-то еще.
Однако обработка специального назначения относится только к самой внешней операции. Составляющие являются нормальными выражениями. Поэтому, когда вы пишете
foo[:][1] = 'two'
он расширяется до
foo.__getitem__(slice(None, None, None)).__setitem__(1, 'two')
часть foo.__getitem__(slice(None, None, None))
создает копию, и эта копия изменяется __setitem__
. Но не исходный массив.
Ответ 2
foo[:]
является копией foo
. Вы мутировали копию.
Ответ 3
Главное отметить здесь, что foo[:]
вернет себе копию, а затем индексирование [1]
будет применено к скопированному списку, который был возвращен
# indexing is applied on copied list
(foo[:])[1] = 'two'
^
copied list
Вы можете просмотреть это, если сохранить ссылку на скопированный список. Таким образом, операцию foo[:][1] = 'two'
можно переписать как:
foo = [1, 2, 3, 4]
# the following is similar to foo[:][1] = 'two'
copy_foo = foo[:]
copy_foo[1] = 'two'
Теперь copy_foo
был изменен:
print(copy_foo)
# [1, 'two', 3, 4]
Но, foo
остается неизменным:
print(foo)
# [1, 2, 3, 4]
В вашем случае вы не указали промежуточный результат при копировании списка foo
с помощью foo[:]
, то есть вы не сохранили ссылку на него. После того как присвоение 'two'
выполняется с помощью foo[:][1] = 'two'
, промежуточный скопированный список перестает существовать.
Ответ 4
Использование
foo[1] = 'two'
и
foo[2:] = ['three', 'four']
и он работает.
Ответ, который находится в комментарии выше (потому что вы используете копию)