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]
. Какой порядок оценки дает эти два результата?
Сначала правая, а потом левая. И с любой стороны порядок оценки слева направо.
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]
f() + g() * h()
функции оцениваются в порядке f, затем g, затем h. Например, a.pop() + a.pop() * a.pop()
с a = [3, 2, 1]
дает 7
(1 + 2 * 3)
- person tobias_k; 13.03.2017
a[-1]
оценивается до того, как значение будет удалено, что дает значение 3
. Затем появляется pop
, также дающее значение 3
. Таким образом, математика 3+3
. Это связано с тем, что в RHS мы работаем слева направо, оценивая каждую часть отдельно, а затем занимаясь математикой.
- person coderforlife; 14.03.2017
+
будет выполнен первым, но это не значит, что d
должен быть оценен первым.
- person Barmar; 14.03.2017
Ключевым моментом является то, что a[-1] += a.pop()
является синтаксическим сахаром для a[-1] = a[-1] + a.pop()
. Это верно, потому что +=
применяется к неизменяемому объекту (здесь int
), а не к изменяемому объекту (здесь соответствующий вопрос).
Правая сторона (RHS) оценивается в первую очередь. На правой стороне: эквивалентный синтаксис a[-1] + a.pop()
. Сначала a[-1]
получает последнее значение 3
. Во-вторых, a.pop()
return
с 3
. 3
+ 3
равно 6
.
Слева (слева) a
теперь равно [1,2]
из-за мутации на месте, уже примененной list.pop()
, поэтому значение a[-1]
изменено с 2
на 6
.
a
- это список целых чисел, как я теперь узнал. Это стоит упомянуть.
- person felipa; 13.03.2017
a
был списком строк или кортежей, он также вел бы себя так же, как целочисленный случай (он отличается только для изменяемых объектов)
- person Chris_Rands; 13.03.2017
Давайте посмотрим на вывод 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 это выглядит немного иначе, но похоже.
dis.dis
, не знал о такой!
- person ppasler; 13.03.2017
Использование тонкой оболочки вокруг списка с отладочными операторами печати может использоваться для отображения порядка оценки в ваших случаях:
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
Заказ:
a[-1]
после =
pop()
, уменьшая длину a
Дело в том, что a[-1]
становится значением a[1]
(было a[2]
) после pop()
, но это происходит до присваивания.
a[0] = a[0] + a.pop()
Работает как положено
a[0]
после =
pop()
Этот пример показывает, почему вы не должны манипулировать списком во время работы с ним (обычно говорят о циклах). В этом случае всегда работайте с копиями.
a[-1] += a.pop()
вa[-1] = a[-1] + a.pop()
. Вот почему вы получаете6
.a[-1]
оценивается передa.pop()
. Если вы измените его наa[-1] = a.pop() + a[-1]
, вы получите5
- person Ma0   schedule 13.03.2017a += b
не совпадает сa = a + b
и не гарантируется, что Python будет переведен как таковой. Вызываются разные методы. Сводится ли это к одному и тому же, зависит от типов операндов. - person Alex Riley   schedule 13.03.2017+
вызывает метод__add__
, тогда как+=
пытается вызвать метод__iadd__
- они могут иметь одинаковый эффект (например, целые числа) или могут немного отличаться (например, списки). - person Alex Riley   schedule 13.03.2017+
и+=
эквивалентны для целых чисел, является ключом к ответу на этот вопрос: общий результат был бы таким же, если бы были задействованы разные объекты. Я думаю, что порядок вычисления главное.) - person Alex Riley   schedule 13.03.2017b = [[5], [3]]; a = b[1]; b[-1] += b.pop(); print (a)
еще больше сбивает с толку! - person felipa   schedule 13.03.2017a = [1, 2, 3]; temp = a.pop(); a[-1] = 2 * temp
илиa = [1, 2, 3]; temp = a.pop(); a[-1] += temp
, в зависимости от того, что вы хотели сделать. Это делает ваш предполагаемый порядок оценки явным, и его легче получить правильно. - person jpmc26   schedule 14.03.2017