Beklenmedik bir şekilde alt listelere yansıtılan liste değişiklikleri listesi

Oct 27 2008

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

614 CAdaker Oct 27 2008 at 22:03

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]*4kez 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] * 4ifade 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] * 4aynı nedenle her zaman [x**2 for x in range(3)]reevaluates x**2her zaman. Her değerlendirme [1] * 4yeni 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 = 21'i 2'ye çeviremezsin.

140 nadrimajstor Aug 27 2013 at 06:17
size = 3
matrix_surprise = [[0] * size] * size
matrix = [[0]*size for i in range(size)]

Canlı Python Eğitmeni Görselleştir

56 PierreBdR Oct 27 2008 at 22:07

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.

38 BlairConrad Oct 27 2008 at 22:02
[[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.

10 Kasravnd Jun 18 2015 at 00:08

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.onesve np.zerosve / 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])
7 ZbyněkWinkler Apr 06 2016 at 20:40

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 adeğ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 cartık listeye aeş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?

7 jerrymouse Apr 06 2017 at 12:36

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 objher 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 . 1Değişmez olmasına rağmen , obj =[1]*4yine de oluşturmak için 14 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 objolacak [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
5 bagrat Jun 10 2015 at 21:38

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 xolan 1ve yiçeren tek bir eleman listesi var x. İlk adımınız y * 4size yeni bir liste getirecek , yani ztemelde [x, x, x, x], yani ilk xnesneye referans olan 4 öğeye sahip yeni bir liste oluşturur . Net adım oldukça benzer. Temelde yapmak z * 3olan [[x, x, x, x]] * 3döner ve [[x, x, x, x], [x, x, x, x], [x, x, x, x]]ilk adım için aynı nedenden dolayı.

5 NeerajKomuravalli Jun 14 2016 at 13:36

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.

4 awulll Apr 24 2016 at 20:31

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]]
4 ouxiaogu Aug 09 2019 at 15:37

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 *3da referanslar oluşturur, ancak referansları değişmezdir, [&0, &0, &0]o zaman ne zaman değiştirileceğini li[0], herhangi bir temel referansı değiştiremezsiniz 0, böylece referans adresini yenisiyle değiştirebilirsiniz &1;
  • iken ma=[&li, &li, &li]ve li, değişebilirdir Aradığınızda yüzden ma[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.
3 AdilAbbasi Aug 10 2016 at 14:09

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] * 2gerç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]]
2 AnandTripathi Jul 15 2016 at 20:48

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
2 DeepakPatankar Jun 21 2020 at 18:34

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] = 5yaratı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

Brian Oct 23 2020 at 02:57

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 shapeargüman) ve 1.0sen yapı (zamanda hiçbiri ile çalışır) ile başlatılır verilsin elemanıdır. initArgü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

mishsx Nov 23 2020 at 02:45

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 [[]] * 3bu 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.