В чем разница между назначением среза, которое разрезает весь список, и прямым назначением?

Я вижу во многих местах использование назначения слайсов для lists. Я могу понять его использование при использовании индексов (не по умолчанию), но я не могу понять его использование, например:

a_list[:] = ['foo', 'bar']

Чем это отличается от

a_list = ['foo', 'bar']

?


person 0xc0de    schedule 14.04.2012    source источник


Ответы (4)


a_list = ['foo', 'bar']

Создает новый list в памяти и указывает на него имя a_list. Неважно, на что a_list указывал раньше.

a_list[:] = ['foo', 'bar']

Вызывает метод __setitem__ объекта a_list с slice в качестве индекса и новый list, созданный в памяти, в качестве значения.

__setitem__ оценивает slice, чтобы выяснить, какие индексы он представляет, и вызывает iter для переданного значения. Затем он выполняет итерацию по объекту, устанавливая каждый индекс в диапазоне, указанном slice, на следующее значение из объекта. Для lists, если диапазон, указанный slice, не имеет той же длины, что и итерируемый, размер list изменяется. Это позволяет вам делать ряд интересных вещей, например удалять разделы списка:

a_list[:] = [] # deletes all the items in the list, equivalent to 'del a_list[:]'

или вставка новых значений в середину списка:

a_list[1:1] = [1, 2, 3] # inserts the new values at index 1 in the list

Однако с «расширенными срезами», где step не равно единице, итерация должна быть правильной длины:

>>> lst = [1, 2, 3]
>>> lst[::2] = []
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
ValueError: attempt to assign sequence of size 0 to extended slice of size 2

Основные отличия в назначении фрагмента a_list:

  1. a_list уже должен указывать на объект
  2. Этот объект изменен, вместо того, чтобы указывать a_list на новый объект
  3. Этот объект должен поддерживать __setitem__ с индексом slice.
  4. Объект справа должен поддерживать итерацию
  5. На объект справа не указано имя. Если на него нет других ссылок (например, когда это литерал, как в вашем примере), он будет считаться несуществующим после завершения итерации.
person agf    schedule 14.04.2012

Разница очень большая! В

a_list[:] = ['foo', 'bar']

Вы изменяете существующий список, привязанный к имени a_list. С другой стороны,

a_list = ['foo', 'bar']

присваивает новому списку имя a_list.

Возможно, это поможет:

a = a_list = ['foo', 'bar'] # another name for the same list
a_list = ['x', 'y'] # reassigns the name a_list
print a # still the original list

a = a_list = ['foo', 'bar']
a_list[:] = ['x', 'y'] # changes the existing list bound to a
print a # a changed too since you changed the object
person Jochen Ritzel    schedule 14.04.2012

Назначая a_list[:], a_list по-прежнему ссылается на тот же объект списка с измененным содержимым. Назначая a_list, a_list теперь ссылается на новый объект списка.

Посмотрите его id:

>>> a_list = []
>>> id(a_list)
32092040
>>> a_list[:] = ['foo', 'bar']
>>> id(a_list)
32092040
>>> a_list = ['foo', 'bar']
>>> id(a_list)
35465096

Как видите, его id не меняется с версией назначения слайса.


Разница между ними может привести к совершенно разным результатам, например, когда список является параметром функции:

def foo(a_list):
    a_list[:] = ['foo', 'bar']

a = ['original']
foo(a)
print(a)

При этом a также изменяется, но если вместо него используется a_list = ['foo', 'bar'], a остается в исходном значении.

person Yu Hao    schedule 09.08.2015

person    schedule
comment
Вопрос касался назначения ['foo', 'bar'] на a_list[:]. - person Gino Mempin; 22.07.2019