Beklenmedik bir şekilde alt listelere yansıtılan liste değişiklikleri listesi
Python'da bir liste listesi oluşturmam gerekti, bu yüzden şunu yazdım:
myList = [[1] * 4] * 3
Liste şuna benziyordu:
[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
Sonra en içteki değerlerden birini değiştirdim:
myList[0][0] = 5
Şimdi listem şöyle görünüyor:
[[5, 1, 1, 1], [5, 1, 1, 1], [5, 1, 1, 1]]
istediğim ya da beklediğim şey bu değil. Lütfen birisi neler olduğunu ve bunun üstesinden nasıl gelineceğini açıklayabilir mi?
Yanıtlar
Yazdığınızda [x]*3
, esasen listeyi alırsınız [x, x, x]
. Yani, aynı 3 referansa sahip bir liste x
. Daha sonra bu single'ı değiştirdiğinizde x
, üç referansın tümü aracılığıyla görülebilir:
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]]
Düzeltmek için her pozisyonda yeni bir liste oluşturduğunuzdan emin olmanız gerekir. Bunu yapmanın bir yolu
[[1]*4 for _ in range(3)]
bir [1]*4
kez değerlendirmek ve 1 listeye 3 referans yapmak yerine her seferinde yeniden değerlendirecek.
*
Liste anlayışının yaptığı gibi neden bağımsız nesneler yapamayacağınızı merak edebilirsiniz. Bunun nedeni, çarpma operatörünün *
ifadeleri görmeden nesneler üzerinde çalışmasıdır. 3 *
ile çarpmak için kullandığınızda [[1] * 4]
, *
yalnızca 1 öğeli listenin [[1] * 4]
, [[1] * 4
ifade metni olarak değerlendirildiğini görür . *
bu öğenin nasıl kopyalanacağı hakkında hiçbir fikri yoktur, nasıl yeniden değerlendirileceğine dair [[1] * 4]
hiçbir fikri yoktur ve kopya isteyeceğiniz hakkında hiçbir fikri yoktur ve genel olarak, öğeyi kopyalamanın bir yolu bile olmayabilir.
Tek seçenek *
, yeni alt listeler yapmaya çalışmak yerine mevcut alt listeye yeni referanslar yapmaktır. Diğer her şey tutarsız olabilir veya temel dil tasarım kararlarının büyük ölçüde yeniden tasarlanmasını gerektirir.
Buna karşılık, bir liste anlama, her yinelemede öğe ifadesini yeniden değerlendirir. [[1] * 4 for n in range(3)]
reevaluates [1] * 4
aynı nedenle her zaman [x**2 for x in range(3)]
reevaluates x**2
her zaman. Her değerlendirme [1] * 4
yeni bir liste oluşturur, böylece liste anlama istediğinizi yapar.
Bu arada, [1] * 4
öğelerini de kopyalamıyor [1]
, ama bu önemli değil, çünkü tamsayılar değişmezdir. Böyle bir şey yapıp 1.value = 2
1'i 2'ye çeviremezsin.
size = 3
matrix_surprise = [[0] * size] * size
matrix = [[0]*size for i in range(size)]
Canlı Python Eğitmeni Görselleştir
Aslında tam da beklediğiniz şey bu. Burada olup biteni ayrıştıralım:
Sen yaz
lst = [[1] * 4] * 3
Bu şuna eşdeğerdir:
lst1 = [1]*4
lst = [lst1]*3
Bu lst
, tümünün işaret ettiği 3 öğenin bulunduğu bir listedir lst1
. Bu, aşağıdaki iki satırın eşdeğer olduğu anlamına gelir:
lst[0][0] = 5
lst1[0] = 5
lst[0]
Hiçbir şey olmadığı gibi lst1
.
İstenen davranışı elde etmek için liste anlamayı kullanabilirsiniz:
lst = [ [1]*4 for n in range(3) ] #python 3
lst = [ [1]*4 for n in xrange(3) ] #python 2
Bu durumda, ifade her n için yeniden değerlendirilir ve farklı bir listeye yol açar.
[[1] * 4] * 3
ya da:
[[1, 1, 1, 1]] * 3
[1,1,1,1]
İç listenin üç kopyası değil , dahili 3 kez referans veren bir liste oluşturur , bu nedenle listeyi her değiştirdiğinizde (herhangi bir konumda) değişikliği üç kez görürsünüz.
Bu örnekle aynı:
>>> 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]]
muhtemelen biraz daha az şaşırtıcı olduğu yerde.
Sorunu doğru bir şekilde açıklayan kabul edilen cevabın yanı sıra, liste anlayışınızda, atılabilir değişken yerine xrange()
daha verimli ( range()
python 3'te aynı işi yapan) bir jeneratör döndüren python-2.x kullanın :_
n
[[1]*4 for _ in xrange(3)] # and in python3 [[1]*4 for _ in range(3)]
Ayrıca, çok daha fazla Pythonic yolu olarak itertools.repeat(), tekrarlanan öğelerden oluşan bir yineleyici nesnesi oluşturmak için kullanabilirsiniz :
>>> a=list(repeat(1,4))
[1, 1, 1, 1]
>>> a[0]=5
>>> a
[5, 1, 1, 1]
Yalnızca olanlar veya sıfır kullanabilirsiniz dizisi oluşturmak istiyorsanız PS, numpy kullanma np.ones
ve np.zeros
ve / veya diğer numara kullanım için np.repeat()
:
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])
Python kapsayıcıları diğer nesnelere referanslar içerir. Bu örneğe bakın:
>>> a = []
>>> b = [a]
>>> b
[[]]
>>> a.append(1)
>>> b
[[1]]
Bu b
, listeye referans olan bir öğe içeren bir listedir a
. Liste a
değiştirilebilir.
Bir listenin bir tamsayı ile çarpılması, listeyi kendisine birden çok kez eklemeye eşdeğerdir (bkz. Yaygın sıralama işlemleri ). Öyleyse örnekle devam edelim:
>>> c = b + b
>>> c
[[1], [1]]
>>>
>>> a[0] = 2
>>> c
[[2], [2]]
Listenin c
artık listeye a
eşdeğer olan iki referans içerdiğini görebiliriz c = b * 2
.
Python SSS, bu davranışın açıklamasını da içerir: Çok boyutlu bir listeyi nasıl oluştururum?
myList = [[1]*4] * 3
[1,1,1,1]
bellekte bir liste nesnesi oluşturur ve referansını 3 kez kopyalar. Bu eşdeğerdir obj = [1,1,1,1]; myList = [obj]*3
. Listede başvurulan obj
her yerde obj
, yapılacak herhangi bir değişiklik üç yerde yansıtılacaktır . Doğru ifade şöyle olacaktır:
myList = [[1]*4 for _ in range(3)]
veya
myList = [[1 for __ in range(4)] for _ in range(3)]
Burada dikkat edilmesi gereken önemli nokta, *
operatörün çoğunlukla değişmez değerlerin bir listesini oluşturmak için kullanılmasıdır . 1
Değişmez olmasına rağmen , obj =[1]*4
yine de oluşturmak için 1
4 kez tekrarlanan bir liste oluşturacaktır [1,1,1,1]
. Ancak değişmez bir nesneye herhangi bir gönderme yapılırsa, nesnenin üzerine yenisi yazılır.
Yaptığımız bu araçlar obj[1]=42
, daha sonra obj
olacak [1,42,1,1]
değil
bazı varsayabiliriz olarak. Bu ayrıca doğrulanabilir:
[42,42,42,42]
>>> myList = [1]*4
>>> myList
[1, 1, 1, 1]
>>> id(myList[0])
4522139440
>>> id(myList[1]) # Same as myList[0]
4522139440
>>> myList[1] = 42 # Since myList[1] is immutable, this operation overwrites myList[1] with a new object changing its id.
>>> myList
[1, 42, 1, 1]
>>> id(myList[0])
4522139440
>>> id(myList[1]) # id changed
4522140752
>>> id(myList[2]) # id still same as myList[0], still referring to value `1`.
4522139440
Kodunuzu aşağıdaki şekilde yeniden yazalım:
x = 1
y = [x]
z = y * 4
myList = [z] * 3
Ardından, her şeyi daha net hale getirmek için aşağıdaki kodu çalıştırın. Kodun yaptığı şey, temel idolarak elde edilen nesnelerin e'lerini yazdırmaktır.
Bir nesnenin "kimliğini" döndürür
ve onları tanımlamamıza ve ne olduğunu analiz etmemize yardımcı olur:
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)))
Ve aşağıdaki çıktıyı alacaksınız:
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
Şimdi adım adım ilerleyelim. Var x
olan 1
ve y
içeren tek bir eleman listesi var x
. İlk adımınız y * 4
size yeni bir liste getirecek , yani z
temelde [x, x, x, x]
, yani ilk x
nesneye referans olan 4 öğeye sahip yeni bir liste oluşturur . Net adım oldukça benzer. Temelde yapmak z * 3
olan [[x, x, x, x]] * 3
döner ve [[x, x, x, x], [x, x, x, x], [x, x, x, x]]
ilk adım için aynı nedenden dolayı.
Basit bir deyişle, bu oluyor çünkü python'da her şey referansa göre çalışıyor , bu yüzden bu şekilde bir liste listesi oluşturduğunuzda temelde bu tür sorunlarla karşılaşıyorsunuz.
Sorununuzu çözmek için aşağıdakilerden birini yapabilirsiniz: 1. numpy.empty için numpy dizi belgelerini kullanın 2. Listeye ulaşırken listeyi ekleyin. 3. İsterseniz sözlüğü de kullanabilirsiniz.
Sanırım herkes neler olduğunu açıklıyor. Çözmek için bir yol öneriyorum:
myList = [[1 for i in range(4)] for j in range(3)]
myList[0][0] = 5
print myList
Ve sonra şunlara sahipsin:
[[5, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
Python liste çarpımından @spelchekr : [[...]] * 3, değiştirildiğinde birbirini yansıtan 3 liste yapar ve "Neden sadece dış * 3 daha fazla referans oluştururken içteki * neden daha fazla referans oluşturuyor ? Neden hepsi 1 değil? "
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]]
İşte yukarıdaki kodu denedikten sonra açıklamam:
- İç kısım
*3
da referanslar oluşturur, ancak referansları değişmezdir,[&0, &0, &0]
o zaman ne zaman değiştirileceğinili[0]
, herhangi bir temel referansı değiştiremezsiniz0
, böylece referans adresini yenisiyle değiştirebilirsiniz&1
; - iken
ma=[&li, &li, &li]
veli
, değişebilirdir Aradığınızda yüzdenma[0][0]=1
, ma [0] [0] eşit olan&li[0]
bütün yüzden&li
örneklerini içine 1 adresini değişecek&1
.
Daha açıklayıcı bir şekilde açıklamaya çalışıyorum,
Operasyon 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]]
Operasyon 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]]
İlk listenin ilk öğesini değiştirmenin, her listenin ikinci öğesini neden değiştirmediğini fark ettiniz mi? Bunun nedeni [0] * 2
gerçekte iki sayıdan oluşan bir listedir ve 0'a yapılan bir referans değiştirilemez.
Klon kopyaları oluşturmak istiyorsanız, İşlem 3'ü deneyin:
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]]
klon kopyaları oluşturmanın bir başka ilginç yolu, İşlem 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]]
Dahili liste işlevini kullanarak bunu yapabilirsiniz
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
Bu soruların birçok cevabı var, aynısını şematik olarak açıklamak için cevabımı ekliyorum.
2D'yi yaratma şekliniz sığ bir liste oluşturur
arr = [[0]*cols]*row
Bunun yerine, listenin öğelerini güncellemek istiyorsanız, kullanmalısınız
rows, cols = (5, 5)
arr = [[0 for i in range(cols)] for j in range(rows)]
Açıklama :
Aşağıdakileri kullanarak bir liste oluşturabilirsiniz:
arr = [0]*N
veya
arr = [0 for i in range(N)]
İlk durumda, dizinin tüm indisleri aynı tamsayı nesnesini gösterir.
Belirli bir dizine değer atadığınızda örn için ve yeni bir int nesnesi oluşturulur arr[4] = 5
yaratır
Şimdi bir liste listesi oluşturduğumuzda ne olacağını görelim, bu durumda üst listemizin tüm öğeleri aynı listeyi gösterecektir.
Ve herhangi bir dizinin değerini güncellerseniz, yeni bir int nesnesi oluşturulacaktır. Ancak tüm üst düzey liste dizinleri aynı listeyi gösterdiğinden, tüm satırlar aynı görünecektir. Ve bir öğeyi güncellemenin o sütundaki tüm öğeleri güncellediği hissine kapılacaksınız.
Kredi: sayesinde Pranav Devarakonda kolay açıklama için buraya
Buraya geldim çünkü keyfi sayıda listeyi nasıl iç içe geçirebileceğimi görmek istiyordum. Yukarıda birçok açıklama ve spesifik örnek var, ancak aşağıdaki özyinelemeli fonksiyonla ... listelerinin listelerinin N boyutlu listesini genelleştirebilirsiniz:
import copy
def list_ndim(dim, el=None, init=None):
if init is None:
init = el
if len(dim)> 1:
return list_ndim(dim[0:-1], None, [copy.copy(init) for x in range(dim[-1])])
return [copy.deepcopy(init) for x in range(dim[0])]
İşleve ilk çağrınızı şu şekilde yaparsınız:
dim = (3,5,2)
el = 1.0
l = list_ndim(dim, el)
burada (3,5,2)
(numpy benzer yapının boyutları olan bir başlık olur shape
argüman) ve 1.0
sen yapı (zamanda hiçbiri ile çalışır) ile başlatılır verilsin elemanıdır. init
Argümanın yalnızca iç içe geçmiş alt listeleri taşımak için özyinelemeli çağrı tarafından sağlandığını unutmayın.
yukarıdaki çıktı:
[[[1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0]],
[[1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0]],
[[1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0]]]
belirli öğeleri ayarlayın:
l[1][3][1] = 56
l[2][2][0] = 36.0+0.0j
l[0][1][0] = 'abc'
ortaya çıkan çıktı:
[[[1.0, 1.0], ['abc', 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0]],
[[1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 56.0], [1.0, 1.0]],
[[1.0, 1.0], [1.0, 1.0], [(36+0j), 1.0], [1.0, 1.0], [1.0, 1.0]]]
listelerin tipsiz yapısı yukarıda gösterilmiştir
Sıradaki öğelerin kopyalanmadığını unutmayın; bunlara birden çok kez başvurulur . Bu genellikle yeni Python programcılarına musallat olur; düşünmek:
>>> lists = [[]] * 3
>>> lists
[[], [], []]
>>> lists[0].append(3)
>>> lists
[[3], [3], [3]]
Olan şey, [[]]
boş bir liste içeren tek öğeli bir listedir, dolayısıyla öğesinin üç öğesi de [[]] * 3
bu tek boş listeye referanslardır. Listelerin herhangi bir öğesinin değiştirilmesi bu tek listeyi değiştirir.
Bunu açıklamak için başka bir örnek, çok boyutlu diziler kullanmaktır .
Muhtemelen bunun gibi çok boyutlu bir dizi yapmaya çalıştınız:
>>> A = [[**None**] * 2] * 3
Yazdırırsanız bu doğru görünüyor:
>>> A
[[None, None], [None, None], [None, None]]
Ancak bir değer atadığınızda, birden çok yerde görünür:
>>> A[0][0] = 5
>>> A
[[5, None], [5, None], [5, None]]
Bunun nedeni, bir listeyi çoğaltmanın *
kopya oluşturmaması, yalnızca mevcut nesnelere referanslar oluşturmasıdır. 3, aynı uzunluktaki iki listeye 3 referans içeren bir liste oluşturur. Bir satırda yapılan değişiklikler tüm satırlarda görünecektir ve bu neredeyse kesinlikle istediğiniz şey değildir.