Ich musste eine Liste mit Listen in Python erstellen, also habe ich Folgendes eingegeben:
myList = [[1] * 4] * 3
Die Liste sah so aus:
[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
Dann habe ich einen der innersten Werte geändert:
myList[0][0] = 5
Nun sieht meine Liste so aus:
[[5, 1, 1, 1], [5, 1, 1, 1], [5, 1, 1, 1]]
das ist nicht das, was ich wollte oder erwartet hatte. Kann jemand bitte erklären, was los ist und wie man es umgehen kann?
Wenn Sie [x]*3
Schreiben, erhalten Sie im Wesentlichen die Liste [x, x, x]
. Das heißt, eine Liste mit 3 Verweisen auf das gleiche x
. Wenn Sie dann dieses einzelne x
ändern, ist es über alle drei Verweise darauf sichtbar.
Um dies zu beheben, müssen Sie sicherstellen, dass Sie an jeder Position eine neue Liste erstellen. Ein Weg dies zu tun ist
[[1]*4 for _ in range(3)]
dies wertet [1]*4
jedes Mal neu aus, anstatt es einmal auszuwerten und 3 Verweise auf 1 Liste zu machen.
Sie fragen sich vielleicht, warum *
Keine unabhängigen Objekte so erstellen kann wie das Listenverständnis. Dies liegt daran, dass der Multiplikationsoperator *
Auf Objekte angewendet wird, ohne Ausdrücke zu sehen. Wenn Sie *
Verwenden, um [[1] * 4]
Mit 3 zu multiplizieren, wird bei *
Nur die Liste mit 1 Elementen angezeigt, bei [[1] * 4]
Jedoch nicht die [[1] * 4
. Ausdruckstext. *
Hat keine Ahnung, wie Kopien dieses Elements erstellt werden sollen, keine Ahnung, wie [[1] * 4]
Neu bewertet werden soll, und keine Ahnung, dass Sie überhaupt Kopien möchten, und im Allgemeinen gibt es möglicherweise nicht einmal eine Möglichkeit zum Kopieren das Element.
Die einzige Option, die *
Hat, besteht darin, neue Verweise auf die vorhandene Unterliste zu erstellen, anstatt zu versuchen, neue Unterlisten zu erstellen. Alles andere wäre inkonsistent oder erfordert eine umfassende Neugestaltung grundlegender Entscheidungen zum Sprachdesign.
Im Gegensatz dazu bewertet ein Listenverständnis den Elementausdruck bei jeder Iteration neu. [[1] * 4 for n in range(3)]
wertet [1] * 4
jedes Mal aus dem gleichen Grund neu aus [x**2 for x in range(3)]
wertet x**2
jedes Mal neu aus. Jede Auswertung von [1] * 4
Erzeugt eine neue Liste, so dass das Listenverständnis das tut, was Sie wollten.
Übrigens kopiert [1] * 4
Auch nicht die Elemente von [1]
, Aber das spielt keine Rolle, da ganze Zahlen unveränderlich sind. Sie können so etwas wie 1.value = 2
Nicht machen und aus einer 1 eine 2 machen.
size = 3
matrix_surprise = [[0] * size] * size
matrix = [[0]*size for i in range(size)]
Eigentlich ist das genau das, was Sie erwarten würden. Lassen Sie uns zerlegen, was hier passiert:
Du schreibst
lst = [[1] * 4] * 3
Dies ist äquivalent zu:
lst1 = [1]*4
lst = [lst1]*3
Dies bedeutet, dass lst
eine Liste mit 3 Elementen ist, die alle auf lst1
Zeigen. Dies bedeutet, dass die beiden folgenden Zeilen äquivalent sind:
lst[0][0] = 5
lst1[0] = 5
Als lst[0]
Ist nichts anderes als lst1
.
Um das gewünschte Verhalten zu erhalten, können Sie das Listenverständnis verwenden:
lst = [ [1]*4 for n in xrange(3) ]
In diesem Fall wird der Ausdruck für jedes n neu ausgewertet, was zu einer anderen Liste führt.
[[1] * 4] * 3
oder auch:
[[1, 1, 1, 1]] * 3
Erstellt eine Liste, die dreimal auf das interne [1,1,1,1]
Verweist - nicht drei Kopien der inneren Liste. Wenn Sie also die Liste (an einer beliebigen Position) ändern, wird die Änderung dreimal angezeigt.
Es ist das gleiche wie in diesem Beispiel:
>>> inner = [1,1,1,1]
>>> outer = [inner]*3
>>> outer
[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
>>> inner[0] = 5
>>> outer
[[5, 1, 1, 1], [5, 1, 1, 1], [5, 1, 1, 1]]
wo es wahrscheinlich ein bisschen weniger überraschend ist.
Wenn Sie Python-2.x verwenden, verwenden Sie neben der akzeptierten Antwort, die das Problem richtig erklärt hat, im Rahmen Ihres Listenverständnisses xrange()
, das einen effizienteren Generator zurückgibt (range()
in = python 3 erledigt den gleichen Job) _
anstelle der Wegwerfvariablen n
:
[[1]*4 for _ in xrange(3)] # and in python3 [[1]*4 for _ in range(3)]
Als viel mehr pythonische Möglichkeit können Sie itertools.repeat()
verwenden, um ein Iterator-Objekt von repeat zu erstellen Elemente:
>>> a=list(repeat(1,4))
[1, 1, 1, 1]
>>> a[0]=5
>>> a
[5, 1, 1, 1]
P.S. Wenn Sie mit numpy nur ein Array aus Einsen oder Nullen erstellen möchten, können Sie np.ones
Und np.zeros
Verwenden und/oder für andere Zahlen np.repeat()
verwenden:
In [1]: import numpy as np
In [2]:
In [2]: np.ones(4)
Out[2]: array([ 1., 1., 1., 1.])
In [3]: np.ones((4, 2))
Out[3]:
array([[ 1., 1.],
[ 1., 1.],
[ 1., 1.],
[ 1., 1.]])
In [4]: np.zeros((4, 2))
Out[4]:
array([[ 0., 0.],
[ 0., 0.],
[ 0., 0.],
[ 0., 0.]])
In [5]: np.repeat([7], 10)
Out[5]: array([7, 7, 7, 7, 7, 7, 7, 7, 7, 7])
In einfachen Worten geschieht dies, weil in python= alles nach Referenz funktioniert , wenn Sie also eine Liste mit Listen erstellen, die So landen Sie im Grunde mit solchen Problemen.
Um Ihr Problem zu lösen, können Sie einen der folgenden Schritte ausführen: 1. Verwenden Sie numpy array Dokumentation für numpy.empty 2. Fügen Sie die Liste an, wenn Sie zu einer Liste gelangen. 3. Sie können auch ein Wörterbuch verwenden, wenn Sie möchten
Python-Container enthalten Verweise auf andere Objekte. Siehe dieses Beispiel:
>>> a = []
>>> b = [a]
>>> b
[[]]
>>> a.append(1)
>>> b
[[1]]
In diesem b
ist eine Liste, die ein Element enthält, das auf die Liste a
verweist. Die Liste a
ist veränderbar.
Die Multiplikation einer Liste mit einer ganzen Zahl entspricht dem mehrfachen Hinzufügen der Liste zu sich selbst (siehe allgemeine Sequenzoperationen ). Fahren Sie also mit dem Beispiel fort:
>>> c = b + b
>>> c
[[1], [1]]
>>>
>>> a[0] = 2
>>> c
[[2], [2]]
Wir können sehen, dass die Liste c
jetzt zwei Verweise auf die Liste a
enthält, was c = b * 2
Entspricht.
Python FAQ enthält auch eine Erklärung zu diesem Verhalten: Wie erstelle ich eine mehrdimensionale Liste?
myList = [[1]*4] * 3
erstellt ein Listenobjekt [1,1,1,1]
im Speicher und kopiert die Referenz dreimal. Dies entspricht obj = [1,1,1,1]; myList = [obj]*3
. Jede Änderung an obj
wird an drei Stellen wiedergegeben, an denen in der Liste auf obj
verwiesen wird. Die richtige Aussage wäre:
myList = [[1]*4 for _ in range(3)]
oder
myList = [[1 for __ in range(4)] for _ in range(3)]
Wichtig hier zu beachten ist, dass *
Der Operator wird hauptsächlich zum Erstellen eines Liste der Literale verwendet. Schon seit 1
ist ein Literal, daher obj =[1]*4
wird erschaffen [1,1,1,1]
wo jeder 1
ist atomar und nicht eine Referenz von 1
4 mal wiederholt. Dies bedeutet, wenn wir obj[2]=42
, dann obj
wird [1,1,42,1]
nicht wie manche vermuten mögen.[42,42,42,42]
Versuchen, es anschaulicher zu erklären,
Operation 1:
x = [[0, 0], [0, 0]]
print(type(x)) # <class 'list'>
print(x) # [[0, 0], [0, 0]]
x[0][0] = 1
print(x) # [[1, 0], [0, 0]]
Operation 2:
y = [[0] * 2] * 2
print(type(y)) # <class 'list'>
print(y) # [[0, 0], [0, 0]]
y[0][0] = 1
print(y) # [[1, 0], [1, 0]]
Bemerkt, warum das erste Element der ersten Liste nicht geändert wurde und nicht das zweite Element jeder Liste geändert wurde? Das ist, weil [0] * 2
ist wirklich eine Liste mit zwei Zahlen, und ein Verweis auf 0 kann nicht geändert werden.
Wenn Sie Klonkopien erstellen möchten, versuchen Sie es mit Vorgang 3:
import copy
y = [0] * 2
print(y) # [0, 0]
y = [y, copy.deepcopy(y)]
print(y) # [[0, 0], [0, 0]]
y[0][0] = 1
print(y) # [[1, 0], [0, 0]]
eine weitere interessante Methode zum Erstellen von Klonkopien, Operation 4:
import copy
y = [0] * 2
print(y) # [0, 0]
y = [copy.deepcopy(y) for num in range(1,5)]
print(y) # [[0, 0], [0, 0], [0, 0], [0, 0]]
y[0][0] = 5
print(y) # [[5, 0], [0, 0], [0, 0], [0, 0]]
Lassen Sie uns Ihren Code folgendermaßen umschreiben:
x = 1
y = [x]
z = y * 4
myList = [z] * 3
Führen Sie dann den folgenden Code aus, um alles klarer zu machen. Der Code gibt im Grunde genommen die id
s der erhaltenen Objekte aus, die
Gibt die "Identität" eines Objekts zurück
und helfen uns, sie zu identifizieren und zu analysieren, was passiert:
print("myList:")
for i, subList in enumerate(myList):
print("\t[{}]: {}".format(i, id(subList)))
for j, elem in enumerate(subList):
print("\t\t[{}]: {}".format(j, id(elem)))
Und Sie erhalten die folgende Ausgabe:
x: 1
y: [1]
z: [1, 1, 1, 1]
myList:
[0]: 4300763792
[0]: 4298171528
[1]: 4298171528
[2]: 4298171528
[3]: 4298171528
[1]: 4300763792
[0]: 4298171528
[1]: 4298171528
[2]: 4298171528
[3]: 4298171528
[2]: 4300763792
[0]: 4298171528
[1]: 4298171528
[2]: 4298171528
[3]: 4298171528
Lassen Sie uns nun Schritt für Schritt voranschreiten. Sie haben x
, das ist 1
, Und eine einzelne Elementliste y
, die x
enthält. Ihr erster Schritt ist y * 4
, Wodurch Sie eine neue Liste z
erhalten, die im Grunde genommen [x, x, x, x]
Ist, dh, es wird eine neue Liste erstellt, die 4 Elemente enthält, auf die verwiesen wird das ursprüngliche x
Objekt. Der Nettoschritt ist ziemlich ähnlich. Grundsätzlich machen Sie z * 3
, Was [[x, x, x, x]] * 3
Ist und [[x, x, x, x], [x, x, x, x], [x, x, x, x]]
Zurückgibt, aus dem gleichen Grund wie für den ersten Schritt.
Ich denke, jeder erklärt, was passiert. Ich schlage einen Lösungsweg vor:
myList = [[1 for i in range(4)] for j in range(3)]
myList[0][0] = 5
print myList
Und dann hast du:
[[5, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
Mit der eingebauten Listenfunktion können Sie dies tun
a
out:[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
#Displaying the list
a.remove(a[0])
out:[[1, 1, 1, 1], [1, 1, 1, 1]]
# Removed the first element of the list in which you want altered number
a.append([5,1,1,1])
out:[[1, 1, 1, 1], [1, 1, 1, 1], [5, 1, 1, 1]]
# append the element in the list but the appended element as you can see is appended in last but you want that in starting
a.reverse()
out:[[5, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
#So at last reverse the whole list to get the desired list
@spelchekr from Python-Listen-Multiplikation: [[...]] * 3 erstellt 3 Listen, die sich gegenseitig spiegeln, wenn sie geändert werden und ich hatte die gleiche Frage zu "Warum erstellt nur die äußere * 3 mehr Referenzen während der innere es nicht tut? Warum ist es nicht alles 1s? "
li = [0] * 3
print([id(v) for v in li]) # [140724141863728, 140724141863728, 140724141863728]
li[0] = 1
print([id(v) for v in li]) # [140724141863760, 140724141863728, 140724141863728]
print(id(0)) # 140724141863728
print(id(1)) # 140724141863760
print(li) # [1, 0, 0]
ma = [[0]*3] * 3 # mainly discuss inner & outer *3 here
print([id(li) for li in ma]) # [1987013355080, 1987013355080, 1987013355080]
ma[0][0] = 1
print([id(li) for li in ma]) # [1987013355080, 1987013355080, 1987013355080]
print(ma) # [[1, 0, 0], [1, 0, 0], [1, 0, 0]]
Hier ist meine Erklärung, nachdem ich den obigen Code ausprobiert habe:
*3
Erzeugt ebenfalls Referenzen, aber seine Referenzen sind unveränderlich, so wie [&0, &0, &0]
. Wenn Sie dann li[0]
Ändern, können Sie keine zugrunde liegende Referenz von const int 0
, Sie können also einfach die Referenzadresse in die neue ändern &1
;ma=[&li, &li, &li]
und li
veränderbar sind, ist ma [0] [0] bei einem Aufruf von ma[0][0]=1
gleichbedeutend mit &li[0]
, also alle &li
- Instanzen ändern ihre erste Adresse in &1
.