“RuntimeError: yineleme sırasında sözlük boyutu değişti” hatası nasıl önlenir?


258

Tüm diğer soruları aynı hatayla kontrol ettim ancak yararlı bir çözüm bulamadım = /

Listeler sözlüğüm var:

d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}

bazı değerlerin boş olduğu. Bu listeleri oluşturmanın sonunda, sözlüğümü döndürmeden önce bu boş listeleri kaldırmak istiyorum. Şu an bu şekilde yapmaya çalışıyorum:

for i in d:
    if not d[i]:
        d.pop(i)

ancak, bu bana çalışma zamanı hatası veriyor. Bir sözlükte yineleme yaparken öğe ekleyemeyeceğinizi / kaldıramayacağınızı biliyorum ... o zaman bunun etrafında bir yol ne olurdu?

Yanıtlar:


455

Python 2.x çağrısında keys, aşağıdakileri değiştirirken tekrarlayabileceğiniz anahtarın bir kopyasını oluşturur dict:

for i in d.keys():

keysBunun bir liste yerine yineleyici döndürdüğünden Python 3.x içinde çalışmadığını unutmayın .

Başka bir yol da yapılacak listanahtarların bir kopyasını zorlamaktır. Bu Python 3.x de çalışır:

for i in list(d):

1
Sanırım 'arama keys, anahtarları kopyalayabileceğiniz anahtarların bir kopyasını yapar ' yani pluralanahtarlar değil mi? Aksi takdirde, tek bir anahtar üzerinden nasıl yineleme yapılabilir? Bu arada nit toplama değilim, gerçekten anahtar veya anahtar olup olmadığını bilmek gerçekten ilgi duyuyorum
HighOnMeat

6
Veya daha hızlı olduğu için liste yerine tuple yapın.
Brambor

8
Python 3.x'in davranışını açıklığa kavuşturmak için, d.keys () yinelenebilir (yineleyici değil ) döndürür , bu da sözlüğün anahtarlarında doğrudan bir görünüm olduğu anlamına gelir. Kullanmak for i in d.keys()aslında genel olarak python 3.x'de çalışır, ancak sözlüğün tuşlarının yinelenebilir bir görünümü üzerinde yinelendiği için d.pop(), döngü sırasında çağrı yapmak bulduğunuzla aynı hataya yol açar. for i in list(d)Sizinki gibi özel durumlar için, tuşları yinelemeden önce bir listeye kopyalamanın biraz verimsiz python 2 davranışını taklit eder.
Michael Krebs

iç dikte içindeki bir nesneyi silmek istediğinizde python3 çözümünüz çalışmaz. örneğin A dikdörtgeni ve A dikdesinde B diktesi var. B diktüsündeki nesneyi silmek istiyorsanız hata oluşur
Ali-T

1
Python3.x olarak, list(d.keys())aynı çıktıyı oluşturur list(d)çağıran listbir üzerinde dictgetiri tuşları. keysÇağrı (pahalı değil gerçi) gereksizdir.
Sean Breckenridge

46

Alakalı öğeleri yeni bir dikte kopyalamak için sözlük anlama özelliğini kullanmanız yeterlidir

>>> d
{'a': [1], 'c': [], 'b': [1, 2], 'd': []}
>>> d = { k : v for k,v in d.iteritems() if v}
>>> d
{'a': [1], 'b': [1, 2]}

Bunun için Python 3'te

>>> d
{'a': [1], 'c': [], 'b': [1, 2], 'd': []}
>>> d = { k : v for k,v in d.items() if v}
>>> d
{'a': [1], 'b': [1, 2]}

11
d.iteritems()bana bir hata verdi. Bunun d.items()yerine - python3 kullanarak
wcyn

4
Bu OP'nin sorusunda ortaya çıkan sorun için geçerlidir. Bununla birlikte, bu RuntimeError'a çok iş parçacıklı kodda vurduktan sonra gelen herkes, CPython'un GIL'inin liste kavramasının ortasında da yayınlanabileceğini ve farklı bir şekilde düzeltmeniz gerektiğini unutmayın.
Yirkha

40

Yalnızca "kopya" kullanmanız gerekir:

Bu şekilde orijinal sözlük alanlarını tekrarlarsınız ve anında istediğiniz dikteyi değiştirebilirsiniz. Her python sürümü üzerinde çalışıyor, bu yüzden daha net.

In [1]: d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}

In [2]: for i in d.copy():
   ...:     if not d[i]:
   ...:         d.pop(i)
   ...:         

In [3]: d
Out[3]: {'a': [1], 'b': [1, 2]}

13

İlk etapta boş listeler eklemekten kaçınmaya çalışırdım, ancak genellikle şunu kullanırdım:

d = {k: v for k,v in d.iteritems() if v} # re-bind to non-empty

2.7'den önce ise:

d = dict( (k, v) for k,v in d.iteritems() if v )

ya da sadece:

empty_key_vals = list(k for k in k,v in d.iteritems() if v)
for k in empty_key_vals:
    del[k]

+1: son seçenek ilginçtir, çünkü yalnızca silinmesi gereken öğelerin anahtarlarını kopyalar. Bu, yalnızca az sayıda öğenin diktenin boyutuna göre silinmesi gerektiğinde daha iyi performans verebilir.
Mark Byers

@MarkByers yup - ve çok sayıda yaparsa, dikdörtgeni filtrelenmiş yeni bir taneye yeniden bağlamak daha iyi bir seçenektir. Her zaman yapının nasıl çalışması gerektiği beklentisi
Jon Clements

4
Yeniden hatırlamanın bir tehlikesi, programın herhangi bir yerinde eski dikte referans tutan bir nesne olması, değişiklikleri görmeyeceğidir. Durum böyle olmadığından eminseniz, o zaman ... bu makul bir yaklaşımdır, ancak orijinal dikteyi değiştirmekle tamamen aynı olmadığını anlamak önemlidir.
Mark Byers

@MarkByers son derece iyi bir nokta - Sen ve ben bunu biliyoruz (ve sayısız diğerleri), ama herkes için açık değil. Ve ben de arkada ısırmadı masanın üzerine para koyacağım :)
Jon Clements

Boş girişleri eklemekten kaçınma noktası çok iyi.
Magnus Bodin

12

Python 3 için:

{k:v for k,v in d.items() if v}

Güzel ve özlü. Benim için Python 2.7'de de çalıştı.
donrondadon

6

Döngü sırasında değiştirilirken sözlük üzerinden yineleme yapamazsınız. Listelemek ve bu listeyi tekrarlamak için bir döküm yapın, benim için çalışıyor.

    for key in list(d):
        if not d[key]: 
            d.pop(key)

1

Bu gibi durumlarda, orijinal dikteyi değiştirirken derin bir kopya yapmak ve bu kopyadan döngü yapmak istiyorum.

Arama alanı bir liste içindeyse, listenin for döngüsünde numaralandırılabilir ve ardından orijinal sözlükte alana erişmek için konumu dizin olarak belirtebilirsiniz.


1

Bu benim için çalıştı:

dict = {1: 'a', 2: '', 3: 'b', 4: '', 5: '', 6: 'c'}
for key, value in list(dict.items()):
    if (value == ''):
        del dict[key]
print(dict)
# dict = {1: 'a', 3: 'b', 6: 'c'}  

Sözlük öğelerini listeye yayınlamak, öğelerinin bir listesini oluşturur, böylece onu yineleyebilir ve RuntimeError.


1

dictc = {"stName": "asas"} tuşları = listedeki anahtarlar için dictc.keys () (anahtarlar): dictc [key.upper ()] = 'Yeni değer' print (str (dictc))


0

Çalışma zamanı hatasının nedeni, yineleme sırasında yapısı değişirken bir veri yapısı üzerinden yineleme yapamamanızdır.

Aradığınızı elde etmenin bir yolu, kaldırmak istediğiniz anahtarları eklemek için listeyi kullanmak ve daha sonra liste boyunca yineleme yaparken tanımlanan anahtarı kaldırmak için sözlükte pop işlevini kullanmaktır.

d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}
pop_list = []

for i in d:
        if not d[i]:
                pop_list.append(i)

for x in pop_list:
        d.pop(x)
print (d)

0

Python 3, sözlüğü tekrarlarken (yukarıdaki döngü için kullanarak) silinmeye izin vermez. Yapılacak çeşitli alternatifler var; basit bir yolu aşağıdaki satırı değiştirmek için

for i in x.keys():

İle

for i in list(x)
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.