Как этот список расширен с назначением нарезки?

Я наткнулся на следующий код (вроде):

my_list = [1, [2, 3, 4], 5]
my_list[1:2] = my_list[1]

После запуска этих двух строк переменная my_list будет [1, 2, 3, 4, 5]. Довольно полезно для расширения вложенных списков.

Но почему он действительно делает то, что он делает?

Я бы предположил, что оператор my_list[1:2] = my_list[1] выполнил одно из следующих действий:

  • просто поместите [2, 3, 4] во вторую позицию в списке (где он уже есть)
  • выдает некоторую ошибку "слишком много значений для распаковки", пытаясь поместить три значения (а именно 2,3,4) в контейнер длиной 1 (а именно my_list[1:2]). (Повторение выше с помощью массива Numpy вместо списка приводит к аналогичной ошибке.)

Другие вопросы (например, Как присваивание работает с фрагментом списка python), как правило, не обращают большого внимания на несоответствие между размером среза, который нужно заменить, и размер элементов, которые вы заменяете. (Не говоря уже о том, почему он работает так, как он это делает.)

Ответ 1

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

Вы на самом деле не назначаете срез, т.е. Python не создает объект среза, который содержит указанные значения из списка, а затем изменяет эти значения. Одна из причин, по которой это не сработает, - это то, что slicing возвращает новый список, поэтому эта операция не изменит исходный список.

Также см. этот вопрос, в котором подчеркивается, что нарезка и назначение срезов совершенно разные.

Ответ 2

Короткий ответ:

my_list[1:2] = my_list[1] заменит содержимое от 1-го индекса на 2-й индекс my_list содержимым, присутствующим в 1-м индексе my_list

Объяснение:

Посмотрим на две операции резки, очень похожие, но совершенно разные

  • Этот создает копию списка и сохраняет переменную

    some_variable = some_list[2:5]
    
  • Этот заменяет содержимое списка inplace, что также позволяет изменить длину списка.

    some_list[2:5] = [1, 2, 3, 4]
    

Когда вы используете оператор присваивания =, он вызывает функцию __setitem__. Здесь основное внимание уделяется рассмотренному выше случаю 2. В соответствии с Python Assignment Statement документ:

Если цель - срез: основное выражение в ссылке оценены. Он должен давать изменяемый объект последовательности (например, список). Назначенный объект должен быть объектом последовательности того же типа. Затем оцениваются нижние и верхние граничные выражения, если они присутствуют; значения по умолчанию равны нулю и длина последовательности. границы должны оцениваться целыми числами. Если какая-либо граница отрицательна, к нему добавляется длина длин. Полученные границы обрезаются до лежат между нулем и длиной последовательностей включительно. Наконец, объект последовательности предлагается заменить срез на элементы назначенная последовательность. Длина среза может отличаться от длины назначенной последовательности, тем самым изменяя длину целевой последовательности, если это позволяет целевая последовательность.

В нашем случае my_list[1:2] = my_list[1], python также вызовет __setitem__ как:

my_list.__setitem__(slice(1,2,None), [2, 3, 4])

Обратитесь slice, чтобы узнать, что он делает.

Итак, когда вы сделали my_list[1:2] = my_list[1], вы заменили содержимое от 1-го индекса на 2-й индекс my_list содержимым, присутствующим в 1-м индексе my_list i.e. [2, 3, 4].


Думаю, теперь мы можем ответить, почему ваши предположения неверны:

  • поместите [2, 3, 4] во вторую позицию в списке (где она уже есть)

Нет. Поскольку __setitem__ не вызывается в индексе, а на slice индексов, которые вы передали.

  • введите некоторую ошибку "слишком много значений для распаковки", от попыток поместить три значения (а именно 2,3,4) в контейнер длиной 1 (а именно: my_list [1: 2]).

Опять Нет. Поскольку диапазон ваших индексов, создающих ваш контейнер, заменяется новым набором значений.

Ответ 3

Вот соответствующий бит из Справочник по языку Python

Если целью является нарезка: основное выражение в ссылке оценены. Он должен давать изменяемый объект последовательности (например, список). Назначенный объект должен быть объектом последовательности того же типа. Затем оцениваются нижние и верхние граничные выражения, если они присутствуют; значения по умолчанию равны нулю и длина последовательности. границы должны оцениваться целыми числами. Если какая-либо граница отрицательна, к нему добавляется длина длин. Полученные границы обрезаются до лежат между нулем и длиной последовательностей включительно. Наконец, объект последовательности предлагается заменить срез на элементы назначенная последовательность. Длина среза может отличаться от длины назначенной последовательности, тем самым изменяя длину целевой последовательности, если это позволяет целевая последовательность.

Такое поведение имеет смысл качественно, потому что, когда вы нарезаете список, вы получаете дополнительный список, чтобы заменить его другим списком, не добавляя уровень вложенности. Разрешение на изменение длины списка является выбором дизайна. Другие варианты возможны, как демонстрирует ваш пример numpy.

Ответ 4

Что вы делаете, это назначение slice.

Назначение срезам также возможно, и это может даже изменить размер списка или полностью очистить его

my_list[1:2] = my_list[1]

Это заменяет срез my_list содержимым my_list[1].

Указав my_list[1:2] в левой части оператора присваивания =, вы сообщаете Python, что вы хотите использовать назначение среза.

my_list[1:2] = my_list[1] эквивалентно my_list.__setitem__(slice(1, 2, None), my_list[1])

В slice (1, 2, None), 1 запускается, 2 останавливается и None является шагом и не является обязательным.

Ответ 5

Что вы здесь пытаетесь назвать Slice Assingnment. В python можно назначить итерируемый (my_list[1] в вашем случае) фрагмент другого итерабельного (my_list[0:1] в вашем случае). Давайте рассмотрим некоторые примеры, чтобы понять, что это на самом деле означает:

 >>> l = [1,2,3,4,5]
 >>> b = [6,7,8]
 >>> l[0:3] = b
 >>> l
 >>> [6, 7, 8, 4, 5]

Итак, что произошло здесь, это часть списка l для 0,1,2 индексов. который охватывает элементы 1,2,3, заменяется элементами списка b 6,7,8. Однако в этом случае размер среза и замененных элементов оказывается случайно.

Итак, что происходит, когда размер среза и итерабельность для замены не равны

>>> l = [1,2,3,4,5]
>>> b = [6,7,8]
>>> l[0:4] = b
>>> l 
>>> [6,7,8,5]

Обратите внимание, что эта операция не произвела никакой ошибки, вместо этого она просто скопировала все элементы, доступные со всей нарезанной частью. В этом случае нарезанные элементы 1,2,3,4 заменяются на 6,7,8

В предыдущем примере итерабельность для замены была меньше. Что произойдет, если часть среза меньше

>>> l = [1,2,3,4,5]
>>> b = [6,7,8]
>>> l[0:1] = b
>>> l 
>>> [6,7,8,2,3,4,5]

Итак, теперь мы видим, что только первый элемент заменяется целым итерабельным b.

Вы также можете использовать это поведение для удаления определенной части списка (что мне удобно в некоторых ситуациях).

 >>> l = [1,2,3,4,5]
 >>> l[0:2] = []
 >>> l 
 >>> [3,4,5]

Здесь легко удаляются первые два элемента.

Итак, пример в вашем вопросе аналогичен приведенным выше примерам, за исключением того, что в вашем случае есть дополнительный шаг для распаковки значений списка. Распаковка списка происходит каждый раз, когда вы назначаете список другому списку. Краткий пример

>>> l = [[1]]
>>> a = []
>>> a = l[0]
>>> a
>>> [1]

Ваш пример:

#Replace the slice [0:1] with my_list[1] perform unpacking list values as well
>>> my_list[1:2] = my_list[1]  
>>> [1,2,3,4,5]

Также обратите внимание, что назначение среза возможно только в том случае, если вы назначаете iterable на срез. Если вы попытаетесь присвоить int или что-то, что не является итерабельным для кусочка python, это приведет к ошибке.

>>> l = [1,2,3,4,5]
>>> b = [6,7,8]
>>> l[0:1] = b[1]
>>> Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
 TypeError: can only assign an iterable

Вот почему в вашем случае my_list[1] не возникает ошибка, так как она является итерируемой.

Ответ 6

То, что вы делаете, это вставить элемент через нарезку. Я объясню все по частям. Более чем вставка может быть интерпретирована как добавление элемента в ваш список после разрезания целевого списка в желаемом диапазоне. Теперь, чтобы подробно объяснить каждую строку:

my_list[1:2]

Эта часть похожа на высказывание Python; "Я хочу получить значения (срез) от индекса 1 до индекса до 2 (исключая 2, я объясню другим примером позже)". После этого вы присваиваете значение этим значениям:

my_list[1:2] = my_list[1] #The same as my_list[1:2] = [2,3,4]

Теперь, когда вы знаете, что делает первая часть, в дальнейшем она добавит элемент в правой части оператора '=', чтобы вы могли интерпретировать его так: "Я хочу отрезать от индекса 1 до всего до 2 (опять же, исключая индекс 2), а затем добавить свой список [2,3,4]". Теперь вот еще несколько примеров, поэтому вы понимаете еще лучше, я надеюсь.

problemList = [1, [2, 3, 4], 5]
problemList[1:2] = problemList[1] #The same as problemList[1:2] = [2,3,4]
analogProblemL = [1] + [2,3,4] + [5] #Output : [1,2,3,4,5]

insertList = [12,13,14]
myList = [1, [2, 3, 4], 5,6,7,8,9]
myList[3:6] = insertList
analogFstList = [1,[2,3,4] ,5] + insertList + [9] #Output : [1,[2,3,4],5,12,13,14,9]

myScnList = [1, [2, 3, 4], 5]
myScnList[1:3] = [2,3,4]
analogScnList = [1] + [2,3,4] + [5] #Output : [1,2,3,4,5]

Следующие строки будут похожи, если бы это были рамки анимации, поэтому было легче интерпретировать:

[1,2,3,4,5] #List before line of code: myList[1:3] = [12,13,14]
[1,|2,3|,4,5] #The moment where you slice the list with: myList[1:3]. Please notice that the characters used '|' are for representing the slice.
[1] + [12,13,14] + [4,5] #After assigning what the slice should be changed for. It like excracting from the whole list the values [2,3] and changing it for [12,13,14].
[1,12,13,14,4,5] #Final list after running the code.

Некоторые ссылки, используемые для этого ответа: http://effbot.org/zone/python-list.htm Понимание нотации фрагмента Python Как присваивание работает с фрагментом списка python

Надеюсь, это было полезно для вас.

Ответ 7

my_list[1:2] = my_list[1], Its simply a inserting statement, it translate to

Мы просто пытаемся найти какой-то элемент между my_list1 из my_list [1] в my_list2. Теперь он очень похож на list.extend(list1), просто мы вставляем между списком в конце