Lorsque vous écrivez, [x]*3
vous obtenez, essentiellement, la liste [x, x, x]
. Autrement dit, une liste avec 3 références à la même chose x
. Lorsque vous modifiez ensuite ce single, x
il est visible via les trois références:
x = [1] * 4
l = [x] * 3
print(f"id(x): {id(x)}")
# id(x): 140560897920048
print(
f"id(l[0]): {id(l[0])}\n"
f"id(l[1]): {id(l[1])}\n"
f"id(l[2]): {id(l[2])}"
)
# id(l[0]): 140560897920048
# id(l[1]): 140560897920048
# id(l[2]): 140560897920048
x[0] = 42
print(f"x: {x}")
# x: [42, 1, 1, 1]
print(f"l: {l}")
# l: [[42, 1, 1, 1], [42, 1, 1, 1], [42, 1, 1, 1]]
Pour y remédier, vous devez vous assurer de créer une nouvelle liste à chaque position. Une façon de le faire est
[[1]*4 for _ in range(3)]
qui réévaluera à [1]*4
chaque fois au lieu de l'évaluer une fois et de faire 3 références à 1 liste.
Vous vous demandez *
peut- être pourquoi vous ne pouvez pas créer des objets indépendants comme le fait la compréhension de la liste. En effet, l'opérateur de multiplication *
opère sur des objets, sans voir d'expressions. Lorsque vous utilisez *
pour multiplier [[1] * 4]
par 3, *
seule la liste à 1 élément est [[1] * 4]
évaluée, pas le [[1] * 4
texte de l' expression. *
n'a aucune idée de comment faire des copies de cet élément, aucune idée de la façon de réévaluer [[1] * 4]
, et aucune idée que vous voulez même des copies, et en général, il pourrait même ne pas y avoir de moyen de copier l'élément.
La seule option *
est de faire de nouvelles références à la sous-liste existante au lieu d'essayer de faire de nouvelles sous-listes. Tout le reste serait incohérent ou nécessiterait une refonte majeure des décisions fondamentales de conception du langage.
En revanche, une compréhension de liste réévalue l'expression de l'élément à chaque itération. [[1] * 4 for n in range(3)]
réévalue à [1] * 4
chaque fois pour la même raison [x**2 for x in range(3)]
réévalue à x**2
chaque fois. Chaque évaluation de [1] * 4
génère une nouvelle liste, donc la compréhension de la liste fait ce que vous vouliez.
Soit dit en passant, [1] * 4
ne copie pas non plus les éléments de [1]
, mais cela n'a pas d'importance, car les entiers sont immuables. Vous ne pouvez pas faire quelque chose comme 1.value = 2
et transformer un 1 en 2.
[x]*3
stocker 3 références comme[x, x, x]
c'est juste quandx
est mutable. Cela ne fonctionne pas pour, par exemplea=[4]*3
, où aprèsa[0]=5
,a=[5,4,4].
[4]*3
est essentiellement équivalent àx = 4; [x, x, x]
. Il est vrai, cependant, que cela ne causera jamais de problème car il4
est immuable. De plus, votre autre exemple n'est pas vraiment différent.a = [x]*3; a[0] = 5
ne causera pas de problèmes même s'ilx
est modifiable, puisque vous ne modifiez pasx
, seulement modifieza
. Je ne décrirais pas ma réponse comme trompeuse ou incorrecte - vous ne pouvez tout simplement pas vous tirer une balle dans le pied si vous avez affaire à des objets immuables.x = 1000; lst = [x]*2; lst[0] is lst[1]
->True
. Python ne fait aucune distinction entre les objets mutables et immuables ici.Live Python Tutor Visualize
la source
x
fait référence. Si vous créez un objet unique au monde avecx = object()
, puismatrix = [[x] * 2]
matrix[0][0] is matrix[0][1]
list
), donc si arow = [x] * 2
que amatrix = [row] * 2
où les deux lignes sont exactement le même objet et se transforme maintenant en une lignematrix[0][0] = y
se reflète soudainement dans l'autre(matrix[0][0] is matrix[1][0]) == True
En fait, c'est exactement ce à quoi vous vous attendez. Décomposons ce qui se passe ici:
vous écrivez
Cela équivaut à:
Cela signifie que
lst
c'est une liste avec 3 éléments pointant tous verslst1
. Cela signifie que les deux lignes suivantes sont équivalentes:Tout
lst[0]
comme rien d'autrelst1
.Pour obtenir le comportement souhaité, vous pouvez utiliser la compréhension de liste:
Dans ce cas, l'expression est réévaluée pour chaque n, conduisant à une liste différente.
la source
id(lst[0][0])
etid(lst[1][0])
ou mêmeid(lst[0])
etid(lst[1])
ou même:
Crée une liste qui référence les
[1,1,1,1]
3 fois internes - pas trois copies de la liste interne, donc chaque fois que vous modifiez la liste (dans n'importe quelle position), vous verrez le changement trois fois.C'est la même chose que cet exemple:
où c'est probablement un peu moins surprenant.
la source
À côté de la réponse acceptée qui a expliqué le problème correctement, dans votre compréhension de la liste, si vous utilisez python-2.x, utilisez
xrange()
un générateur plus efficace (range()
en python 3 fait le même travail)_
au lieu de la variable jetablen
:De plus, en tant que méthode beaucoup plus Pythonique , vous pouvez utiliser
itertools.repeat()
pour créer un objet itérateur d'éléments répétés:PS En utilisant numpy, si vous voulez seulement créer un tableau de ceux ou de zéros que vous pouvez utiliser
np.ones
etnp.zeros
et / ou pour toute autre utilisation du numéronp.repeat()
:la source
Les conteneurs Python contiennent des références à d'autres objets. Voir cet exemple:
Dans
b
c'est une liste qui contient un élément qui est une référence à la listea
. La listea
est modifiable.La multiplication d'une liste par un entier équivaut à ajouter la liste à elle-même plusieurs fois (voir les opérations de séquence courantes ). Donc, continuons avec l'exemple:
Nous pouvons voir que la liste
c
contient maintenant deux références à lista
qui est équivalent àc = b * 2
.La FAQ Python contient également une explication de ce comportement: Comment créer une liste multidimensionnelle?
la source
myList = [[1]*4] * 3
crée un objet liste[1,1,1,1]
en mémoire et copie sa référence 3 fois. C'est équivalent àobj = [1,1,1,1]; myList = [obj]*3
. Toute modification àobj
sera reflétée à trois endroits, où qu'elleobj
soit référencée dans la liste. La bonne déclaration serait:ou
Une chose importante à noter ici est que l'
*
opérateur est principalement utilisé pour créer une liste de littéraux . Bien qu'il1
soit immuable,obj =[1]*4
il créera toujours une liste de1
répétitions 4 fois pour se former[1,1,1,1]
. Mais si une référence à un objet immuable est faite, l'objet est remplacé par un nouveau.Cela signifie que si nous le faisons
obj[1]=42
, celaobj
ne deviendra[1,42,1,1]
pascomme certains peuvent le penser. Cela peut également être vérifié:[42,42,42,42]
la source
obj[2] = 42
remplace la référence à l'index2
, par opposition à la mutation de l'objet référencé par cet index, ce qui est lemyList[2][0] = ...
cas (myList[2]
est une liste, et l'assignation modifie la référence à l'index 0 dans la liste). Bien sûr, les entiers ne sont pas modifiables, mais de nombreux types d'objets le sont . Et notez que la[....]
notation d'affichage de liste est également une forme de syntaxe littérale! Ne confondez pas les objets composés (tels que les listes) et scalaires (tels que les entiers), avec des objets mutables et immuables.En termes simples, cela se produit car en python, tout fonctionne par référence , donc lorsque vous créez une liste de liste de cette façon, vous vous retrouvez avec de tels problèmes.
Pour résoudre votre problème, vous pouvez effectuer l'une des opérations suivantes: 1. Utilisez la documentation du tableau numpy pour numpy.empty 2. Ajoutez la liste au fur et à mesure que vous accédez à une liste. 3. Vous pouvez également utiliser le dictionnaire si vous le souhaitez
la source
Laissez-nous réécrire votre code de la manière suivante:
Ensuite, exécutez le code suivant pour que tout soit plus clair. Ce que le code fait, c'est essentiellement imprimer les
id
s des objets obtenus, quiet nous aidera à les identifier et à analyser ce qui se passe:
Et vous obtiendrez la sortie suivante:
Alors maintenant, allons-y étape par étape. Vous avez
x
qui est1
, et une seule liste d'élémentsy
contenantx
. Votre première étape consiste ày * 4
obtenir une nouvelle listez
, c'est-à[x, x, x, x]
-dire qu'elle crée une nouvelle liste qui comprendra 4 éléments, qui sont des références à l'x
objet initial . L'étape nette est assez similaire. Vous faites essentiellementz * 3
, ce qui est[[x, x, x, x]] * 3
et revient[[x, x, x, x], [x, x, x, x], [x, x, x, x]]
, pour la même raison que pour la première étape.la source
Je suppose que tout le monde explique ce qui se passe. Je suggère une façon de le résoudre:
myList = [[1 for i in range(4)] for j in range(3)]
print myList
Et puis vous avez:
la source
Essayer de l'expliquer de manière plus descriptive,
Opération 1:
Opération 2:
Vous avez remarqué pourquoi la modification du premier élément de la première liste n'a pas modifié le deuxième élément de chaque liste? C'est parce que c'est
[0] * 2
vraiment une liste de deux nombres, et une référence à 0 ne peut pas être modifiée.Si vous souhaitez créer des copies clonées, essayez l'opération 3:
une autre façon intéressante de créer des copies de clones, l'opération 4:
la source
@spelchekr de la multiplication des listes Python: [[...]] * 3 fait 3 listes qui se reflètent lorsqu'elles sont modifiées et j'avais la même question à propos de "Pourquoi seulement l'extérieur * 3 crée-t-il plus de références alors que l'intérieur ne fait pas "Pourquoi ce n'est pas tous les 1?"
Voici mon explication après avoir essayé le code ci-dessus:
*3
crée également des références, mais ses références sont immuables, quelque chose comme[&0, &0, &0]
, alors quand changerli[0]
, vous ne pouvez pas changer une référence sous-jacente de const int0
, vous pouvez donc simplement changer l'adresse de référence en la nouvelle&1
;ma=[&li, &li, &li]
etli
est mutable, donc lorsque vous appelezma[0][0]=1
, ma [0] [0] est également égal à&li[0]
, donc toutes les&li
instances changeront sa 1ère adresse en&1
.la source
En utilisant la fonction de liste intégrée, vous pouvez faire comme ça
la source
a.insert(0,[5,1,1,1])