Kiedy piszesz [x]*3
, dostajesz w zasadzie listę [x, x, x]
. To znaczy lista z 3 odniesieniami do tego samego x
. Kiedy następnie zmodyfikujesz ten singiel, x
będzie on widoczny poprzez wszystkie trzy odniesienia do niego:
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]]
Aby to naprawić, musisz utworzyć nową listę w każdej pozycji. Jednym ze sposobów na to jest
[[1]*4 for _ in range(3)]
który za [1]*4
każdym razem będzie ponownie oceniać, zamiast oceniać go raz i czyniąc 3 odniesienia do 1 listy.
Możesz się zastanawiać, dlaczego *
nie można tworzyć niezależnych obiektów w taki sposób, jak robi to lista. Jest tak, ponieważ operator mnożenia *
działa na obiektach, nie widząc wyrażeń. Kiedy używasz *
do pomnożenia [[1] * 4]
przez 3, *
widzi tylko listę 1-elementową [[1] * 4]
, a nie [[1] * 4
tekst wyrażenia. *
nie ma pojęcia, jak wykonać kopię tego elementu, nie ma pojęcia, jak dokonać ponownej oceny [[1] * 4]
, i nie ma pojęcia, że chcesz kopii, a ogólnie rzecz biorąc, może nie być nawet sposobu na skopiowanie elementu.
Jedyną opcją *
jest tworzenie nowych odniesień do istniejącej listy podrzędnej zamiast prób tworzenia nowych list podrzędnych. Wszystko inne byłoby niespójne lub wymagałoby poważnego przeprojektowania podstawowych decyzji dotyczących projektowania języka.
W przeciwieństwie do tego, zrozumienie listy ponownie ocenia wyrażenie elementu przy każdej iteracji. [[1] * 4 for n in range(3)]
ponownej waloryzacji [1] * 4
za każdym razem z tego samego powodu [x**2 for x in range(3)]
ponownej waloryzacji x**2
za każdym razem. Każda ocena [1] * 4
generuje nową listę, więc zrozumienie listy robi to, co chciałeś.
Nawiasem mówiąc, [1] * 4
również nie kopiuje elementów [1]
, ale to nie ma znaczenia, ponieważ liczby całkowite są niezmienne. Nie możesz zrobić czegoś takiego 1.value = 2
i zmienić 1 w 2.
[x]*3
przechowuj 3 odnośniki, tak jak[x, x, x]
jest właściwe tylko wtedy, gdyx
można je modyfikować. To dzieło nie podlega na przykłada=[4]*3
, gdzie poa[0]=5
,a=[5,4,4].
[4]*3
jest zasadniczo równoważne zx = 4; [x, x, x]
. Prawdą jest jednak, że nigdy nie spowoduje to żadnego problemu, ponieważ4
jest niezmienny. Twój drugi przykład nie jest tak naprawdę innym przypadkiem.a = [x]*3; a[0] = 5
nie spowoduje problemów, nawet jeślix
można je modyfikowaćx
, ponieważ nie modyfikujesz , tylko modyfikujesza
. Nie opisałbym mojej odpowiedzi jako wprowadzającej w błąd lub błędnej - po prostu nie możesz strzelić sobie w stopę, jeśli masz do czynienia z niezmiennymi przedmiotami.x = 1000; lst = [x]*2; lst[0] is lst[1]
->True
. Python nie rozróżnia tutaj w żaden sposób obiektów zmiennych i niezmiennych.Wizualizacja Live Python Tutor
źródło
x
do którego się odnosi. Jeślix = object()
matrix = [[x] * 2]
matrix[0][0] is matrix[0][1]
list
), więc jeślirow = [x] * 2
a następniematrix = [row] * 2
gdzie oba rzędy są dokładnie tym samym obiektem, a teraz zmiany w jednym rzędziematrix[0][0] = y
nagle odzwierciedlają się w drugim(matrix[0][0] is matrix[1][0]) == True
W rzeczywistości jest to dokładnie to, czego można oczekiwać. Rozłóżmy to, co się tutaj dzieje:
Ty piszesz
Jest to równoważne z:
Oznacza to, że
lst
jest to lista z 3 elementami wskazującymi nalst1
. Oznacza to, że dwie następujące linie są równoważne:Jak
lst[0]
niclst1
.Aby uzyskać pożądane zachowanie, możesz skorzystać ze zrozumienia listy:
W takim przypadku wyrażenie jest ponownie oceniane dla każdego n, co prowadzi do innej listy.
źródło
id(lst[0][0])
aid(lst[1][0])
nawetid(lst[0])
id(lst[1])
lub nawet:
Tworzy listę, która odwołuje się do wewnętrznej
[1,1,1,1]
3 razy - nie do trzech kopii wewnętrznej listy, więc za każdym razem, gdy zmodyfikujesz listę (w dowolnej pozycji), zobaczysz zmianę trzy razy.Jest taki sam jak w tym przykładzie:
gdzie jest to chyba trochę mniej zaskakujące.
źródło
Oprócz zaakceptowanej odpowiedzi, która poprawnie wyjaśniła problem, w ramach twojej listy, jeśli używasz python-2.x użyj,
xrange()
która zwraca generator, który jest bardziej wydajny (range()
w python 3 wykonuje to samo zadanie)_
zamiast zmiennej jednorazowejn
:Ponadto, jako o wiele bardziej Pythoński sposób, możesz użyć
itertools.repeat()
do utworzenia obiektu iteratora powtarzających się elementów:PS Korzystanie numpy, jeśli chcesz tylko utworzyć tablicę jedynek lub zer można użyć
np.ones
anp.zeros
i / lub o innym przeznaczeniu numernp.repeat()
:źródło
Kontenery Python zawierają odniesienia do innych obiektów. Zobacz ten przykład:
Na tej
b
liście znajduje się jeden element, który jest odniesieniem do listya
. Listaa
jest zmienna.Mnożenie listy przez liczbę całkowitą jest równoważne wielokrotnemu dodawaniu listy do siebie (patrz typowe operacje na sekwencjach ). Kontynuując przykład:
Widzimy, że lista
c
zawiera teraz dwa odniesienia do listy,a
które są równoważnec = b * 2
.Python FAQ zawiera także wyjaśnienie tego zachowania: Jak utworzyć listę wielowymiarową?
źródło
myList = [[1]*4] * 3
tworzy jeden obiekt listy[1,1,1,1]
w pamięci i trzykrotnie kopiuje jego odwołanie. Jest to równoważne zobj = [1,1,1,1]; myList = [obj]*3
. Wszelkie modyfikacjeobj
zostaną odzwierciedlone w trzech miejscach, niezależnie od tego, gdzieobj
znajduje się odniesienie na liście. Właściwym stwierdzeniem byłoby:lub
Ważną rzeczą do zapamiętania jest to, że
*
operator jest najczęściej używany do tworzenia listy literałów . Chociaż1
jest niezmienny,obj =[1]*4
nadal tworzy listę1
powtarzanych 4 razy do utworzenia[1,1,1,1]
. Ale jeśli pojawi się jakakolwiek wzmianka o niezmiennym obiekcie, obiekt zostanie nadpisany nowym.Oznacza to, że jeśli to zrobimy
obj[1]=42
, nieobj
stanie się[1,42,1,1]
tak,jak niektórzy mogą przypuszczać. Można to również zweryfikować:[42,42,42,42]
źródło
obj[2] = 42
zastępuje odwołanie w indeksie2
, w przeciwieństwie do mutowania obiektu, do którego odwołuje się ten indeks, co właśniemyList[2][0] = ...
robi (myList[2]
jest listą, a przypisanie zmienia referencję w indeksie 0 na liście). Oczywiście liczby całkowite nie są zmienne, ale istnieje wiele typów obiektów . I zauważ, że[....]
notacja wyświetlania listy jest również formą dosłownej składni! Nie należy mylić obiektów złożonych (takich jak listy) i skalarnych (takich jak liczby całkowite) z obiektami zmiennymi i niezmiennymi.Krótko mówiąc, dzieje się tak, ponieważ w Pythonie wszystko działa przez odniesienie , więc kiedy tworzysz listę w ten sposób, w zasadzie masz takie problemy.
Aby rozwiązać problem, możesz wykonać jedną z nich: 1. Użyj dokumentacji tablic numpy dla numpy.empty 2. Dołącz listę, gdy przejdziesz do listy. 3. Możesz także użyć słownika, jeśli chcesz
źródło
Pozwól nam przepisać Twój kod w następujący sposób:
Następnie uruchom następujący kod, aby wszystko było bardziej jasne. To, co robi kod, to w zasadzie wypisywanie
id
s uzyskanych obiektów, którei pomoże nam je zidentyfikować i przeanalizować, co się stanie:
Otrzymasz następujące dane wyjściowe:
Przejdźmy teraz krok po kroku. Masz,
x
co jest1
, i listę pojedynczych elementówy
zawierającąx
. Pierwszym krokiem jesty * 4
uzyskanie nowej listyz
, która jest w zasadzie[x, x, x, x]
, tzn. Tworzy nową listę, która będzie zawierać 4 elementy, które są odniesieniami do początkowegox
obiektu. Krok netto jest dość podobny. Zasadniczo robisz toz * 3
, co jest[[x, x, x, x]] * 3
i powraca[[x, x, x, x], [x, x, x, x], [x, x, x, x]]
, z tego samego powodu, co dla pierwszego kroku.źródło
Chyba wszyscy tłumaczą, co się dzieje. Sugeruję jeden ze sposobów rozwiązania tego problemu:
myList = [[1 for i in range(4)] for j in range(3)]
print myList
A potem masz:
źródło
Próbując wyjaśnić to bardziej opisowo,
Operacja 1:
Operacja 2:
Zauważyłeś, dlaczego modyfikowanie pierwszego elementu pierwszej listy nie modyfikowało drugiego elementu każdej listy? Jest tak, ponieważ
[0] * 2
tak naprawdę jest to lista dwóch liczb, a odniesienia do 0 nie można modyfikować.Jeśli chcesz utworzyć klonowane kopie, wypróbuj Operację 3:
inny ciekawy sposób tworzenia kopii klonowanych, Operacja 4:
źródło
@spelchekr z mnożenia listy w Pythonie: [[...]] * 3 tworzy 3 listy, które po zmodyfikowaniu odzwierciedlają się nawzajem, a ja miałem to samo pytanie dotyczące „Dlaczego tylko zewnętrzne * 3 tworzą więcej referencji, podczas gdy wewnętrzne nie „Dlaczego to nie wszystkie 1?”
Oto moje wyjaśnienie po wypróbowaniu powyższego kodu:
*3
tworzy również referencje, ale jego referencje są niezmienne, coś w rodzaju[&0, &0, &0]
: wtedy, kiedy się zmieniali[0]
, nie można zmienić żadnego bazowego odwołania const int0
, więc wystarczy zmienić adres referencyjny na nowy&1
;ma=[&li, &li, &li]
ili
można go modyfikować, więc gdy zadzwoniszma[0][0]=1
, ma [0] [0] jest równe&li[0]
, więc wszystkie&li
instancje zmienią swój pierwszy adres na&1
.źródło
Korzystając z wbudowanej funkcji listy, możesz to zrobić w ten sposób
źródło
a.insert(0,[5,1,1,1])