web-dev-qa-db-fra.com

Quel est l'ordre d'évaluation dans python lors de l'utilisation de pop (), list [-1] et + =?

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?

39
felipa

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]
38
Fallen

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()returns 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.

22
Chris_Rands

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.

16
tobias_k

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é.

6
MSeifert

Pour votre exemple spécifique

a[-1] += a.pop() #is the same as 
a[-1] = a[-1] + a.pop() # a[-1] = 3 + 3

Ordre:

  1. évaluer a[-1] après =
  2. pop(), diminuant la longueur de a
  3. une addition
  4. affectation

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

  1. évaluer a[0] après =
  2. pop()
  3. une addition
  4. affectation

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.

4
ppasler