a = [1, 2, 3]
a[-1] += a.pop()
Cela se traduit par [1, 6]
.
a = [1, 2, 3]
a[0] += a.pop()
Cela se traduit par [4, 2]
. Quel ordre d'évaluation donne ces deux résultats?
RHS d'abord, puis LHS. Et de toute façon, l'ordre d'évaluation est de gauche à droite.
a[-1] += a.pop()
est identique à, a[-1] = a[-1] + a.pop()
a = [1,2,3]
a[-1] = a[-1] + a.pop() # a = [1, 6]
Voyez comment le comportement change lorsque nous changeons l'ordre des opérations chez RHS,
a = [1,2,3]
a[-1] = a.pop() + a[-1] # a = [1, 5]
L'aperçu clé est que a[-1] += a.pop()
est le sucre syntaxique pour a[-1] = a[-1] + a.pop()
. Cela est vrai car +=
Est appliqué à un objet immuable (un int
ici) plutôt qu'à un objet modifiable ( question pertinente ici ).
Le côté droit (RHS) est évalué en premier. Sur le RHS: la syntaxe équivalente est a[-1] + a.pop()
. Tout d'abord, a[-1]
Obtient la dernière valeur 3
. Deuxièmement, a.pop()
return
s 3
. 3
+ 3
Est 6
.
Sur le côté gauche (LHS), a
est maintenant [1,2]
En raison de la mutation en place déjà appliquée par list.pop()
et donc la valeur de a[-1]
est passé de 2
à 6
.
Jetons un œil à la sortie de dis.dis
pour 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)
La signification des différentes instructions est indiquée ici .
Tout d'abord, LOAD_FAST
Et LOAD_CONST
Chargent a
et -1
Sur la pile, et DUP_TOP_TWO
Duplique les deux, avant BINARY_SUBSCR
obtient la valeur en indice, ce qui donne a, -1, 3
sur la pile. Il charge ensuite à nouveau a
et LOAD_ATTR
Charge la fonction pop
, qui est appelée sans argument par CALL_FUNCTION
. La pile est maintenant a, -1, 3, 3
Et INPLACE_ADD
Ajoute les deux premières valeurs. Enfin, ROT_THREE
Fait pivoter la pile vers 6, a, -1
Pour correspondre à l'ordre attendu par STORE_SUBSCR
Et la valeur est stockée.
Donc, en bref, la valeur actuelle de a[-1]
Est évaluée avant d'appeler a.pop()
et le résultat de l'addition est ensuite stocké dans le nouveau a[-1]
, Quel que soit son courant valeur.
1) Il s'agit du démontage de Python 3, légèrement compressé pour mieux tenir sur la page, avec une colonne ajoutée montrant la pile après # ...
; pour Python 2, cela semble un peu différent, mais similaire.
L'utilisation d'une enveloppe mince autour d'une liste avec des instructions d'impression de débogage peut être utilisée pour montrer l'ordre d'évaluation dans vos cas:
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
Quand je lance maintenant votre exemple:
>>> 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
Il commence donc par obtenir le dernier élément, qui est 3, puis fait apparaître le dernier élément qui est également 3, les ajoute et écrase le dernier élément de votre liste avec 6
. La liste finale sera donc [1, 6]
.
Et dans votre deuxième cas:
>>> 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
Cela prend maintenant le premier élément (1
) l'ajoute à la valeur ajoutée (3
) et écrase le premier élément avec la somme: [4, 2]
.
L'ordre général d'évaluation est déjà expliqué par @Fallen
et @tobias_k
. Cette réponse complète simplement le principe général qui y est mentionné.
Pour votre exemple spécifique
a[-1] += a.pop() #is the same as
a[-1] = a[-1] + a.pop() # a[-1] = 3 + 3
Ordre:
a[-1]
après =
pop()
, diminuant la longueur de a
Le fait est que a[-1]
Devient la valeur de a[1]
(Était a[2]
) Après la pop()
, mais cela se produit avant l'affectation.
a[0] = a[0] + a.pop()
Fonctionne comme prévu
a[0]
après =
pop()
Cet exemple montre pourquoi vous ne devez pas manipuler une liste pendant que vous travaillez dessus (communément appelé pour les boucles). Toujours travailler sur des copies dans ce cas.