111 votes

Quelle est la différence entre i = i + 1 et i += 1 dans une boucle "for" ?

J'ai découvert une chose curieuse aujourd'hui et je me demandais si quelqu'un pouvait m'éclairer sur la différence entre les deux.

import numpy as np

A = np.arange(12).reshape(4,3)
for a in A:
    a = a + 1

B = np.arange(12).reshape(4,3)
for b in B:
    b += 1

Après l'exécution de chaque for boucle, A n'a pas changé, mais B a été ajouté à chaque élément. En fait, j'utilise le B pour écrire dans un tableau NumPy initialisé à l'intérieur d'un fichier for boucle.

113voto

MSeifert Points 6307

La différence réside dans le fait que l'on modifie la structure des données elle-même (opération sur place). b += 1 tandis que l'autre se contente de réaffecte la variable a = a + 1 .


Par souci d'exhaustivité :

x += y es pas toujours en effectuant une opération in-place, il y a (au moins) trois exceptions :

  • Si x ne met pas en œuvre un __iadd__ alors la méthode x += y est une abréviation de x = x + y . Ce serait le cas si x était quelque chose comme un int .

  • Si __iadd__ retours NotImplemented Python revient à x = x + y .

  • En __iadd__ pourrait théoriquement être mise en œuvre pour ne pas fonctionner sur place. Ce serait vraiment bizarre de faire cela, cependant.

Il se trouve que votre b sont numpy.ndarray qui met en œuvre __iadd__ et se renvoie lui-même, de sorte que votre deuxième boucle modifie le tableau original sur place.

Vous trouverez plus d'informations à ce sujet dans le Documentation Python de "Emulating Numeric Types" (en anglais) .

Ces [ __i*__ Les méthodes [ ] sont appelées pour mettre en œuvre les affectations arithmétiques augmentées ( += , -= , *= , @= , /= , //= , %= , **= , <<= , >>= , &= , ^= , |= ). Ces méthodes doivent tenter d'effectuer l'opération sur place (en modifiant self) et renvoyer le résultat (qui peut être, mais pas obligatoirement, self). Si une méthode spécifique n'est pas définie, l'affectation augmentée revient aux méthodes normales. Par exemple, si x est une instance d'une classe avec une méthode __iadd__() méthode, x += y est équivalent à x = x.__iadd__(y) . Autrement, x.__add__(y) y y.__radd__(x) sont prises en compte, comme pour l'évaluation des x + y . Dans certaines situations, l'affectation augmentée peut entraîner des erreurs inattendues (voir Pourquoi les a_tuple[i] += ["item"] soulève une exception lorsque l'ajout fonctionne ? ), mais ce comportement fait en fait partie du modèle de données.

28voto

Maroun Maroun Points 31217

Dans le premier exemple, vous réaffectez la variable a tandis que dans la seconde, vous modifiez les données sur place, à l'aide de la fonction += de l'opérateur.

Voir la section sur les 7.2.1. Instructions d'affectation augmentées :

Une expression d'affectation augmentée telle que x += 1 peut être réécrite comme suit x = x + 1 pour obtenir un effet similaire, mais pas exactement égal. Dans la version augmentée, x n'est évalué qu'une seule fois. De plus, lorsque c'est possible, l'opération est effectuée sur place. ce qui signifie qu'au lieu de créer un nouvel objet et de l'affecter à la cible, l'ancien objet est modifié.

+= appels de l'opérateur __iadd__ . Cette fonction effectue la modification sur place, et ce n'est qu'après son exécution que le résultat est renvoyé à l'objet auquel vous "appliquez" la fonction += sur.

__add__ quant à lui, prend les paramètres et renvoie leur somme (sans les modifier).

13voto

jmd_dk Points 3743

Comme nous l'avons déjà souligné, b += 1 mises à jour b en place, tandis que a = a + 1 calcule a + 1 et attribue ensuite le nom a au résultat (maintenant a ne fait pas référence à une ligne de A plus).

Pour comprendre la += Cependant, pour être un bon opérateur, il faut aussi comprendre la notion de mutable par rapport à immuable objets. Voyons ce qui se passe si nous laissons de côté l'élément .reshape :

C = np.arange(12)
for c in C:
    c += 1
print(C)  # [ 0  1  2  3  4  5  6  7  8  9 10 11]

Nous constatons que C es pas mis à jour, ce qui signifie que c += 1 y c = c + 1 sont équivalents. Cela s'explique par le fait que maintenant C est un tableau 1D ( C.ndim == 1 ), et donc lorsque l'on itère sur C chaque élément entier est extrait et assigné à c .

En Python, les entiers sont immuables, ce qui signifie que les mises à jour sur place ne sont pas autorisées. c += 1 en c = c + 1 donde c se réfère désormais à un nouveau entier, non couplé à C de quelque manière que ce soit. Lorsque vous passez en boucle sur les tableaux remodelés, des lignes entières ( np.ndarray ) sont attribués aux b (et a ) à la fois, qui sont mutable ce qui signifie que vous êtes autorisé à insérer de nouveaux entiers à votre guise, ce qui se produit lorsque vous utilisez des objets a += 1 .

Il convient de mentionner que, bien que les + y += sont censés être liés comme décrit ci-dessus (et le sont très généralement), chaque type peut les mettre en œuvre comme il le souhaite en définissant le paramètre __add__ y __iadd__ respectivement.

4voto

Inconnu Points 3663

La forme courte( a += 1 ) a la possibilité de modifier a au lieu de créer un nouvel objet représentant la somme et de le lier à nouveau au même nom ( a = a + 1 Ainsi, la forme courte ( a += 1 ) est beaucoup plus efficace car il n'a pas nécessairement besoin de faire une copie de a contrairement à a = a + 1 .

De même, même s'ils produisent le même résultat, vous remarquerez qu'ils sont différents parce qu'il s'agit d'opérateurs distincts : + y +=

3voto

Andi Kleve Points 70

Tout d'abord : Les variables a et b dans les boucles font référence à numpy.ndarray objets.

Dans la première boucle, a = a + 1 est évaluée comme suit : le __add__(self, other) fonction de numpy.ndarray est appelé. Cela crée un nouvel objet et, par conséquent, A n'est pas modifié. Ensuite, la variable a est défini pour faire référence au résultat.

Dans la deuxième boucle, aucun nouvel objet n'est créé. L'instruction b += 1 appelle le __iadd__(self, other) fonction de numpy.ndarray qui modifie le ndarray objet en place auquel b fait référence. D'où, B est modifié.

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