a = [1, 2, 3]
a[-1] += a.pop()
В результате получается [1, 6]
.
a = [1, 2, 3]
a[0] += a.pop()
В результате получается [4, 2]
. Какой порядок оценки дает эти два результата?
a = [1, 2, 3]
a[-1] += a.pop()
В результате получается [1, 6]
.
a = [1, 2, 3]
a[0] += a.pop()
В результате получается [4, 2]
. Какой порядок оценки дает эти два результата?
RHS сначала, а затем LHS. И с любой стороны, порядок оценки оставлен вправо.
a[-1] += a.pop()
совпадает с a[-1] = a[-1] + a.pop()
a = [1,2,3]
a[-1] = a[-1] + a.pop() # a = [1, 6]
Посмотрите, как изменяется поведение при изменении порядка операций в RHS,
a = [1,2,3]
a[-1] = a.pop() + a[-1] # a = [1, 5]
Ключевым понятием является то, что a[-1] += a.pop()
является синтаксическим сахаром для a[-1] = a[-1] + a.pop()
. Это верно, потому что +=
применяется к неизменяемому объекту (здесь int
), а не к изменяемому объекту (соответствующий вопрос здесь).
Сначала оценивается правая часть (RHS). В RHS эквивалентный синтаксис a[-1] + a.pop()
. Во-первых, a[-1]
получает последнее значение 3
. Во-вторых, a.pop()
return
3
.
3
+ 3
- 6
.
В левой части (LHS) a
теперь [1,2]
из-за мутации in-place, уже примененной list.pop()
, и поэтому значение a[-1]
изменяется от 2
до 6
.
Посмотрим на вывод dis.dis
для a[-1] += a.pop()
1):
3 15 LOAD_FAST 0 (a) # a,
18 LOAD_CONST 5 (-1) # a, -1
21 DUP_TOP_TWO # a, -1, a, -1
22 BINARY_SUBSCR # a, -1, 3
23 LOAD_FAST 0 (a) # a, -1, 3, a
26 LOAD_ATTR 0 (pop) # a, -1, 3, a.pop
29 CALL_FUNCTION 0 (0 positional, 0 keyword pair) # a, -1, 3, 3
32 INPLACE_ADD # a, -1, 6
33 ROT_THREE # 6, a, -1
34 STORE_SUBSCR # (empty)
Значение различных инструкций указано здесь.
Сначала LOAD_FAST
и LOAD_CONST
загрузите a
и -1
в стек, а DUP_TOP_TWO
дублирует два, прежде чем BINARY_SUBSCR
получит значение индекса, что приведет к a, -1, 3
в стеке. Затем он снова загружает a
, а LOAD_ATTR
загружает функцию pop
, которая вызывается без аргументов с помощью CALL_FUNCTION
. Стек теперь a, -1, 3, 3
, а INPLACE_ADD
добавляет два верхних значения. Наконец, ROT_THREE
поворачивает стек до 6, a, -1
в соответствии с порядком, ожидаемым STORE_SUBSCR
, и значение сохраняется.
Итак, короче, текущее значение a[-1]
оценивается перед вызовом a.pop()
, и результат добавления затем сохраняется обратно к новому a[-1]
, независимо от его текущего значения.
1) Это разборка для Python 3, слегка сжатая для лучшей подгонки на странице, с добавленным столбцом, показывающим стек после # ...
; для Python 2 это выглядит немного иначе, но похоже.
Использование тонкой обертки вокруг списка с отладочными отпечатками печати можно использовать для отображения порядка оценки в ваших случаях:
class Test(object):
def __init__(self, lst):
self.lst = lst
def __getitem__(self, item):
print('in getitem', self.lst, item)
return self.lst[item]
def __setitem__(self, item, value):
print('in setitem', self.lst, item, value)
self.lst[item] = value
def pop(self):
item = self.lst.pop()
print('in pop, returning', item)
return item
Когда я сейчас запустил ваш пример:
>>> a = Test([1, 2, 3])
>>> a[-1] += a.pop()
in getitem [1, 2, 3] -1
in pop, returning 3
in setitem [1, 2] -1 6
Итак, он начинается с получения последнего элемента, который равен 3, затем выдает последний элемент, который также является 3, добавляет их и перезаписывает последний элемент вашего списка с помощью 6
. Таким образом, окончательный список будет [1, 6]
.
И во втором случае:
>>> a = Test([1, 2, 3])
>>> a[0] += a.pop()
in getitem [1, 2, 3] 0
in pop, returning 3
in setitem [1, 2] 0 4
Теперь этот первый элемент (1
) добавляет его к всплываемому значению (3
) и перезаписывает первый элемент с помощью суммы: [4, 2]
.
Общий порядок оценки уже объясняется @Fallen
и @tobias_k
. Этот ответ просто дополняет общий принцип, упомянутый там.
Для конкретного примера
a[-1] += a.pop() #is the same as
a[-1] = a[-1] + a.pop() # a[-1] = 3 + 3
Order:
a[-1]
после =
pop()
, уменьшая длину a
Дело в том, что a[-1]
становится значением a[1]
(было a[2]
) после pop()
, но это происходит до назначения.
a[0] = a[0] + a.pop()
Работает как ожидалось
a[0]
после =
pop()
В этом примере показано, почему вы не должны манипулировать списком во время работы над ним (обычно для циклов). Всегда работайте с копиями в этом случае.