web-dev-qa-db-fra.com

Est-ce que i = i + n est vraiment identique à i + = n?

Un bloc de code fonctionne mais l'autre ne fonctionne pas. Ce qui aurait du sens si le deuxième bloc n’est identique au premier qu’avec une opération écrite en sténographie. Ils sont pratiquement la même opération.

l = ['table']
i = []

Version 1

for n in l:
    i += n
print(i)

Sortie: ['t', 'a', 'b', 'l', 'e']

Version 2

for n in l:
    i = i + n
print(i)

Sortie:

TypeError: ne peut concaténer qu'une liste (pas "str") à une liste


Qu'est-ce qui cause cette erreur étrange?

55
Luke Bakare

Ils ne doivent pas être les mêmes.

L'utilisation de l'opérateur + appelle la méthode __add__ tandis que l'opérateur += appelle __iadd__. C’est entièrement à l’objet en question qu’il incombe de faire appel à l’une de ces méthodes.

Si vous utilisez x += y mais que x ne fournit pas de méthode __iadd__ (ou si la méthode renvoie NotImplemented), __add__ est utilisé comme fallback , ce qui signifie que x = x + y se produit.

Dans le cas de listes, utiliser l += iterable étend la liste l aux éléments de iterable. Dans votre cas, chaque caractère de la chaîne (qui est une variable itérable) est ajouté au cours de l'opération extend.

Démo 1: utilisation de __iadd__

>>> l = []
>>> l += 'table'
>>> l
['t', 'a', 'b', 'l', 'e']

Démo 2: utiliser extend fait la même chose

>>> l = []
>>> l.extend('table')
>>> l
['t', 'a', 'b', 'l', 'e']

Démo 3: l'ajout d'une liste et d'une chaîne soulève un TypeError.

>>> l = []
>>> l = l + 'table'
[...]
TypeError: can only concatenate list (not "str") to list

Si vous n'utilisez pas +=, vous obtenez la TypeError ici car seul __iadd__ implémente le comportement d'extension.

Démo 4: piège courant: += ne construit pas de nouvelle liste. Nous pouvons le confirmer en vérifiant que les identités d'objet sont égales avec l'opérateur is.

>>> l = []
>>> l_ref = l # another name for l, no data is copied here
>>> l += [1, 2, 3] # uses __iadd__, mutates l in-place
>>> l is l_ref # confirm that l and l_ref are names for the same object
True
>>> l
[1, 2, 3]
>>> l_ref # mutations are seen across all names
[1, 2, 3]

Cependant, la syntaxe l = l + iterable crée une nouvelle liste.

>>> l = []
>>> l_ref = l # another name for l, no data is copied here
>>> l = l + [1, 2, 3] # uses __add__, builds new list and reassigns name l
>>> l is l_ref # confirm that l and l_ref are names for different objects
False
>>> l
[1, 2, 3]
>>> l_ref
[]

Dans certains cas, cela peut produire des bogues subtils, car += mute la liste originale, alors que
l = l + iterable crée une nouvelle liste et réaffecte le nom l.

BONUS

Le défi de Ned Batchelder de trouver cela dans la documentation

79
timgeb

Si dans le second cas, vous enroulez une liste autour de n pour éviter les erreurs:

for n in l:
    i = i + [n]
print(i)

vous obtenez

['table']

Donc, ce sont des opérations différentes.

3
Jake

Non.

7.2.1. Déclarations d'affectation augmentées :

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

3
msc