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()В этом примере показано, почему вы не должны манипулировать списком во время работы над ним (обычно для циклов). Всегда работайте с копиями в этом случае.