40 votes

Quel est l'ordre d'évaluation en python avec pop (), list [-1] et + =?

 a = [1, 2, 3]
a[-1] += a.pop()
 

Cela donne [1, 6] .

 a = [1, 2, 3]
a[0] += a.pop()
 

Cela donne [4, 2] . Quel ordre d'évaluation donne ces deux résultats?

39voto

Fallen Points 2162

RHS d'abord, puis LHS. Et de n'importe quel côté, 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]
 

22voto

Chris_Rands Points 15161

L'idée clé est que l' a[-1] += a.pop() est sucre syntaxique pour a[-1] = a[-1] + a.pop(). Cela est vrai parce qu' += est appliquée à un objet immuable (un int ici) plutôt que d'un objet mutable (question pertinente ici).

La droite (D) est évalué en premier. Sur les RHS: l'équivalent de la syntaxe 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 désormais [1,2] en raison de la mutation est déjà appliqué par list.pop() et la valeur de a[-1] est modifié à partir d' 2 de 6.

17voto

tobias_k Points 13121

Jetons un coup d'oeil à la sortie de l' 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 répertorié ici.

Tout d'abord, LOAD_FAST et LOAD_CONST charge a et -1 sur la pile, et DUP_TOP_TWO doublons les deux, avant d' BINARY_SUBSCR obtient l'indice de la valeur, résultant en a, -1, 3 sur la pile. Il charge alors a nouveau, et LOAD_ATTR des charges de l' pop de la fonction, qui est appelé sans argument, en CALL_FUNCTION. La pile est maintenant a, -1, 3, 3, et INPLACE_ADD ajoute les deux valeurs. Enfin, ROT_THREE tourne la pile d' 6, a, -1 pour correspondre à l'ordre prévu par STORE_SUBSCR et la valeur est stockée.

Donc, en résumé, la valeur courante de a[-1] est évaluée avant d'appeler a.pop() et le résultat de l'addition est ensuite stocké retour à la nouvelle - a[-1], indépendamment de sa valeur actuelle.


1) C'est le démontage pour Python 3, légèrement comprimé, afin de mieux l'adapter à la page, avec un ajout de la colonne montrant la pile après l' # ...; pour Python 2 c'est un peu différent, mais semblable.

6voto

MSeifert Points 6307

À l'aide d'un mince wrapper autour d'une liste avec le débogage d'impression-états peut être utilisé pour montrer l'ordre d'évaluation dans votre 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

Lorsque j'ai maintenant exécuter 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

Donc, il commence par se faire le dernier élément, qui est de 3, puis retire le dernier élément qui est aussi 3, les ajoute et écrase le dernier élément de votre liste avec 6. De sorte que la liste définitive sera [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 désormais le premier élément (1) ajoute à la sauté valeur (3) et remplace le premier élément avec la somme: [4, 2].


L'ordre général de l'évaluation est déjà expliqué en @Fallen et @tobias_k. Cette réponse juste, complète le principe général mentionné.

4voto

ppasler Points 2345

Pour vous un exemple précis

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(), la diminution de la longueur de l' a
  3. plus
  4. affectation

Le truc c'est qu' a[-1] devient la valeur de a[1] (a a[2]) après l' pop(), mais ce qui se passe avant la cession.

a[0] = a[0] + a.pop() 

Fonctionne comme prévu

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

Cet exemple montre pourquoi vous ne devriez pas manipuler une liste tout en travaillant sur elle (on le dit couramment pour les boucles). Toujours travailler sur copys dans ce cas.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X