Як призначення призначене для роботи з фрагментом списку Python?


99

Python doc каже, що нарізка списку повертає новий список.
Тепер, якщо повертається "новий" список, у мене є такі сумніви, пов'язані з "Призначенням скибочок"

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

Тепер вихід буде:

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

@Mark Longair Вибачте, я думав, що тільки формат коду повинен бути відформатований, а не вихідний
Kartik Anand


7
Ви розумієте, що a[0] = 4б зробили?
Джош Лі

1
Призначення @KartikAnand Slice - це спеціальний сценарій, коли новий список не створюється. Немає сенсу створювати об’єкт без прив’язки імені з лівої сторони =, тому замість того, щоб відкинути це як недійсний синтаксис, python перетворює його на щось подібне до того, що ви можете очікувати. Оскільки у python немає посилань, це не допоможе, щоб результат фрагмента змінив початковий список. Ви отримуєте копію. Якщо ви надали більше інформації про свою заявку, ми можемо краще допомогти вам робити «пітонічний» спосіб. :)
Кейсі Кубалл

1
@Darthfett Я не працюю над жодною програмою зараз, швидше я навчаю пітона, перш ніж почати бруднити руки :)
Kartik Anand

Відповіді:


114

Ви плутаєте дві різні операції, які використовують дуже подібний синтаксис:

1) нарізка:

b = a[0:2]

Це робить копію фрагмента aі присвоює йому b.

2) призначення зрізів:

a[0:2] = b

Це замінює фрагмент aз вмістом b.

Хоча синтаксис схожий (я уявляю за дизайном!), Це дві різні операції.


4
Ось у чому я сумніваюсь, у другому випадку, чому не є фрагмент нового списку ??
Картік Ананд

11
@KartikAnand Тому що це не так. Це не те, що вказує мова.
Марцін

Щоб було зрозуміло, "бере шматочок" дійсно означає "зробити копію фрагмента", звідки походить частина плутанини.
Марк Викуп

2
@KartikAnand: В основному, так. Перекладач знає, що це таке, і поводиться з ними належним чином.
NPE

1
@Dubslow: це можна зробити за допомогою модуля itertools . У вашому випадку використовувати функцію Ісліце , з start=1, stop=None. Це дозволить уникнути будь-яких копій і використовувати ледачу оцінку (у вашому випадку лінивий доступ до оригінального списку).
Спірос

66

Коли ви вказуєте aв лівій частині =оператора, ви використовуєте звичайне призначення Python , яке змінює ім'я aв поточному контексті, щоб вказувати на нове значення. Це не змінює попереднє значення, на яке aвказували.

Вказуючи a[0:2]в лівій частині =оператора, ви повідомляєте Python, що ви хочете використовувати призначення зрізів . Призначення фрагмента - це спеціальний синтаксис списків, де ви можете вставляти, видаляти або замінювати вміст зі списку:

Вставка :

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

Завдяки розпакуванню каналу ви можете змінювати непослідовні списки:

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

Однак розпакування кортежу обмежується заміною, оскільки ви не можете вставити або видалити елементи.

До і після всіх цих операцій aскладається той самий точний список. Python просто забезпечує приємний синтаксичний цукор для зміни списку на місці.


6
Подібні, але не тотожні, оскільки ви можете мати неоднакову кількість елементів зліва та справа.
Марк Викуп

@MarkRansom Це прекрасний момент, я додав більше інформації, щоб зробити це очевидним.
Кейсі Кубалл

2
Є чи a[:] = some_listеквівалент a = some_list[:]або a = some_list?
jadkik94

2
@ jadkik94 Ні. a[:] = some_listвстановлює кожен елемент елемента aбути таким some_list. Виконання будь-якого з тих, кого ви згадуєте, змінило б те, що aє. Наприклад: a = [1, 2, 3] b = a a[:] = [4, 5, 6] a is b. Останній рядок був би помилковим, якби він змінив aзначення, а не мутував його.
Кейсі Кубалл

@Darthfett Цікаво, я знайшов інакше :) Спасибі
jadkik94

25

Я раніше стикався з тим самим питанням, яке пов'язане із специфікацією мови. Згідно із завданнями-заявами ,

  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

Нарізавши ліву частину операції по призначенню, ви вказуєте, яким елементам слід призначити.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.