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 = []
for n in l:
i += n
print(i)
Sortie: ['t', 'a', 'b', 'l', 'e']
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?
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 quel = 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
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.
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 formex = 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é.