For döngüsü sırasında liste girişleri nasıl değiştirilir?


178

Şimdi yinelemeli bir döngü sırasında listeyi değiştirmenin güvenli olmadığını biliyorum. Ancak, dizelerin bir listesi var ve dizeleri kendileri şerit istiyorum. Değişken değerlerin değiştirilmesi değişiklik olarak kabul edilir mi?


20
Bir dize değiştirilebilir bir değer oluşturmaz.
user470379

4
@ user470379: Listedeki öğelerin değiştirilebilir olup olmadığı, döngü halindeyken listeyi değiştirmenin güvenli olup olmadığı ile ilgili değildir.
martineau

Yanıtlar:


144

Kötü form olarak kabul edilir. Listeye var olan referansları tutmanız gerekirse, dilim atamasıyla birlikte bir liste kavrayışı kullanın.

a = [1, 3, 5]
b = a
a[:] = [x + 2 for x in a]
print(b)

10
Dilim ataması zekidir ve döngü sırasında orijinalin değiştirilmesini önler, ancak orijinalin uzunluğu için geçici bir liste oluşturulmasını gerektirir.
martineau

11
neden b = a veriyoruz?
Vigrond

9
@Vigrond: İfade print byürütüldüğünde, adeğiştirilmek yerine yerinde değiştirilip değiştirilmediğini anlayabilirsiniz . Başka bir olasılık, her print b is aikisinin de hala aynı nesneye atıfta bulunup bulunmadığını görmek olacaktır.
martineau

12
neden a [:] = ve sadece a = değil?
kdubs

10
@kdubs: "... listeye var olan referansları saklamanız gerekiyorsa dilim atamasıyla."
Ignacio Vazquez-Abrams

163

Aşağıdaki döngü yalnızca daha önce görülen öğeleri değiştirdiğinden, kabul edilebilir olarak kabul edilir:

a = ['a',' b', 'c ', ' d ']

for i, s in enumerate(a):
    a[i] = s.strip()

print(a) # -> ['a', 'b', 'c', 'd']

Hangisi farklı:

a[:] = [s.strip() for s in a]

daha fazla indeksleme işlemi gerektirmesine rağmen, orijinal bir listenin oluşturulmasını ve orijinalin yerini almasını gerektirmez.

Dikkat: Girişleri bu şekilde değiştirebilmenize rağmen list, sorunla karşılaşma şansını riske atmadan öğelerin sayısını değiştiremezsiniz .

İşte ne demek istediğime bir örnek - bir giriş silindiğinde, o noktadan itibaren dizin oluşturma işlemi bozulur:

b = ['a', ' b', 'c ', ' d ']

for i, s in enumerate(b):
    if s.strip() != b[i]:  # leading or trailing whitespace?
        del b[i]

print(b)  # -> ['a', 'c ']  # WRONG!

(Sonuç yanlıştır, çünkü olması gereken tüm öğeleri silmemiştir.)

Güncelleme

Bu oldukça popüler bir cevap olduğundan, "yerinde" girişleri nasıl etkili bir şekilde silebilirsiniz (bu tam olarak soru olmamasına rağmen):

b = ['a',' b', 'c ', ' d ']

b[:] = [entry for entry in b if entry.strip() == entry]

print(b)  # -> ['a']  # CORRECT

3
Python neden sözdizimindeki tek bir öğenin yalnızca bir kopyasını oluşturuyor for i in a? Bu çok mantıksız, görünüşte diğer dillerden farklı ve kodumda uzun bir süre hata ayıklamak zorunda kalmama neden oldu. Python Tutorial bundan bile bahsetmiyor. Buna bir neden olmalı olsa da?
xji

1
@JIXiang: Kopya oluşturmuyor. Sadece döngü değişkeni adını ardışık öğelere veya yinelenen şeyin değerine atar.
martineau

1
Neden olmasın aynı satırda aynı nesne için iki isim ( a[i]ve s) kullanıyorsun? Çok tercih ederim a[i] = a[i].strip().
Navin

3
@Navin: Çünkü a[i] = s.strip()sadece bir indeksleme işlemi var.
martineau

1
@martineau enumerate(b)her yinelemede bir dizin oluşturma işlemi yapar ve siz de başka bir işlem gerçekleştirirsiniz a[i] =. AFAIK, bu döngüyü döngü yineleme başına yalnızca 1 dizinleme işlemi ile Python'a uygulamak imkansızdır :(
Navin

18

Döngü varyantı için bir tane daha, bana enumerate () ile olandan daha temiz görünüyor:

for idx in range(len(list)):
    list[idx]=... # set a new value
    # some other code which doesn't let you use a list comprehension

19
Birçoğu range(len(list))Python'da olduğu gibi bir kod kokusu kullanmayı düşünüyor .
martineau

2
@Reishin: enumerateBir jeneratör olduğu için bir liste oluşturmuyor. Tuples'ın bir listesi, liste boyunca tekrarlanırken birer birer oluşturur. Hangisinin daha yavaş olduğunu söylemenin tek yolu olurdu timeit.
martineau

3
@martineau kodu iyi olmayabilir, ama göre timeit enumerateyavaş
Reishin

2
@Reishin: Kıyaslama kodunuz tamamen geçerli değildir, çünkü verilen dizindeki listedeki değeri alma gereğini dikkate almaz - bu da bu cevapta gösterilmez.
martineau

4
@Reishin: Karşılaştırmanız tam da bu nedenle geçersiz. Döngü yükünü izolasyonda ölçüyor. Kesin olmak için, tüm döngünün yürütülmesi için geçen süre ölçülmelidir, çünkü herhangi bir havai farkın, döngü döngüsünün içindeki koda belirli bir yolla sağlanan faydalar ile azaltılması olasılığı vardır - aksi takdirde elmaları karşılaştırmazsınız. elmalar.
martineau

11

Listeye öğe ekleme / kaldırma işlemini değiştirmediğiniz sürece, bir listeyi tekrarlarken her öğeyi değiştirmek iyidir.

Liste kavrayışını kullanabilirsiniz:

l = ['a', ' list', 'of ', ' string ']
l = [item.strip() for item in l]

veya sadece C-stylefor döngüsünü yapın:

for index, item in enumerate(l):
    l[index] = item.strip()

4

Hayır, dizeleri bu şekilde değiştirebilirseniz listenin "içeriğini" değiştiremezsiniz. Ancak Python'da değişmezler. Herhangi bir dize işlemi yeni bir dize döndürür.

Değişken olduğunu bildiğiniz nesnelerin bir listesi varsa, listenin gerçek içeriğini değiştirmediğiniz sürece bunu yapabilirsiniz.

Böylece bir tür harita yapmanız gerekecek. Bir jeneratör ifadesi kullanırsanız, yineleme yaptığınızda [işlem] yapılır ve hafızadan tasarruf edersiniz.


4

Bunun gibi bir şey yapabilirsiniz:

a = [1,2,3,4,5]
b = [i**2 for i in a]

Bir liste içinde döngü kurmanızı kolaylaştırmak için buna liste kavrama denir.


3

Jemshit Iskenderov ve Ignacio Vazquez-Abrams'ın verdiği cevap gerçekten çok iyi. Bu örnekle daha fazla açıklanabilir:

a) Size iki vektör içeren bir liste verilir;

b) listeden geçmek ve dizilerin her birinin sırasını ters çevirmek istersiniz

Diyelim ki var

v = np.array([1, 2,3,4])
b = np.array([3,4,6])

for i in [v, b]:
    i = i[::-1]   # this command does not reverse the string

print([v,b])

Alacaksın

[array([1, 2, 3, 4]), array([3, 4, 6])]

Öte yandan, eğer

v = np.array([1, 2,3,4])
b = np.array([3,4,6])

for i in [v, b]:
   i[:] = i[::-1]   # this command reverses the string

print([v,b])

Sonuç

[array([4, 3, 2, 1]), array([6, 4, 3])]

1

Sorunuzdan hangi dizelerin kaldırılacağına karar verme ölçütlerinin ne olduğu açık değildir, ancak kaldırmak istediğiniz dizelerin bir listesine sahipseniz veya bunları listeleyebiliyorsanız aşağıdakileri yapabilirsiniz:

my_strings = ['a','b','c','d','e']
undesirable_strings = ['b','d']
for undesirable_string in undesirable_strings:
    for i in range(my_strings.count(undesirable_string)):
        my_strings.remove(undesirable_string)

bu benim dizelerimi ['a', 'c', 'e'] olarak değiştirir


0

Kısacası, aynı listeyi tekrarlarken listede değişiklik yapmak.

list[:] = ["Modify the list" for each_element in list "Condition Check"]

misal:

list[:] = [list.remove(each_element) for each_element in list if each_element in ["data1", "data2"]]
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.