"런타임 오류 : 반복하는 동안 사전 크기 변경"오류를 피하는 방법은 무엇입니까?


258

동일한 오류로 다른 모든 질문을 확인했지만 유용한 해결책을 찾지 못했습니다 = /

목록 사전이 있습니다.

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

일부 값은 비어 있습니다. 이 목록을 만들 때 사전을 반환하기 전에 빈 목록을 제거하고 싶습니다. 현재 다음과 같이 이것을하려고합니다.

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

그러나 이것은 런타임 오류를 발생시킵니다. 나는 그것을 반복하는 동안 사전에 요소를 추가 / 제거 할 수 없다는 것을 알고 있습니다 ...이 주위의 방법은 무엇입니까?

답변:


454

Python 2.x에서 호출 keys은 다음을 수정하는 동안 반복 할 수있는 키의 사본을 만듭니다 dict.

for i in d.keys():

keys목록 대신 반복자를 반환 하기 때문에 Python 3.x에서는 작동하지 않습니다 .

다른 방법은 list키 사본을 작성하는 데 사용하는 것입니다. 이것은 Python 3.x에서도 작동합니다.

for i in list(d):

1
나는 당신이 ' 를 반복 keys해서 호출 할 수 있는 의 복사본을 만드는 것 '을 의미한다고 생각 plural합니까? 그렇지 않으면 어떻게 하나의 키를 반복 할 수 있습니까? 그건 그렇고 니트 따기 아니에요, 그것이 진정한 키인지 아닌지 정말로 알고 싶습니다
HighOnMeat

6
또는 목록 대신 튜플이 빠릅니다.
브 람보르

8
python 3.x의 동작을 명확히하기 위해 d.keys ()는 iteratorable이 아닌 iterable을 반환합니다. 이는 iterable이 아니라 사전의 키를 직접 볼 수 있음을 의미합니다. 사용 for i in d.keys()은 실제로 python 3.x 에서 실제로 작동 하지만 사전 키의 반복 가능한보기를 반복하기 때문에 d.pop()루프 중에 호출 하면 발견 한 것과 동일한 오류가 발생합니다. for i in list(d)당신과 같은 특별한 상황에서 반복하기 전에 키를 목록에 복사하는 약간 비효율적 인 파이썬 2 동작을 에뮬레이트합니다.
Michael Krebs

내부 dict에서 객체를 삭제하려는 경우 python3 솔루션이 작동하지 않습니다. 예를 들어 dict A에 dic A와 dict B가 있습니다. dict B에서 객체를 삭제하려면 오류가 발생합니다.
Ali-T

1
python3.x list(d.keys())에서와 동일한 출력을 생성하고를 list(d)호출 하면 키 listdict반환됩니다. keys호출 (비싼 것을 생각) 할 필요가 없습니다.
Sean Breckenridge

46

사전 이해를 사용하여 관련 항목을 새 dict에 복사하십시오.

>>> 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]}

이것을 위해 파이썬 3에서

>>> 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()나에게 오류를 주었다. 내가 사용 d.items()하는 대신 - python3를 사용하여
wcyn

4
이것은 OP의 질문에 제기 된 문제에 효과적입니다. 그러나 멀티 스레드 코드 에서이 RuntimeError를 누른 후 여기에 온 사람은 CPython의 GIL이 목록 이해의 중간에 릴리스 될 수 있으며 다르게 수정해야한다는 점에 유의하십시오.
Yirkha

40

"복사"만 사용하면됩니다.

그렇게하면 원래 사전 필드를 반복하고 원하는 dict (d dict)을 즉시 변경할 수 있습니다. 각 파이썬 버전에서 작동하므로 더 명확합니다.

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]}

일했다! 감사합니다.
Guilherme Carvalho Lithg

13

처음에는 빈 목록을 삽입하지 않으려 고하지만 일반적으로 다음을 사용합니다.

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

2.7 이전의 경우 :

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

또는 그냥 :

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

+1 : 마지막 옵션은 삭제해야하는 항목의 키만 복사하므로 흥미 롭습니다. dict의 크기에 비해 적은 수의 항목 만 삭제해야하는 경우 성능이 향상 될 수 있습니다.
Mark Byers

@MarkByers yup-많은 수의 경우 dict를 필터링 된 새 dict에 다시 바인딩하는 것이 더 좋습니다. 항상 구조가 어떻게 작동해야하는지에 대한 기대입니다
Jon Clements

4
리 바인딩의 한 가지 위험은 프로그램의 어딘가에 이전 명령에 대한 참조를 보유한 객체가 있으면 변경 사항을 볼 수 없다는 것입니다. 그것이 사실이 아니라고 확신한다면, 합리적인 접근 방법이지만, 원래의 dict를 수정하는 것과 완전히 동일하지 않다는 것을 이해하는 것이 중요합니다.
Mark Byers

@MarkByers 매우 좋은 지적-당신과 나는 (그리고 다른 많은 사람들) 알고 있지만, 모두에게 분명하지는 않습니다. 그리고 나는 또한 당신을 물지 않은 테이블에 돈을 넣을 것입니다 :)
Jon Clements

빈 항목을 삽입하지 않는 것이 좋습니다.
Magnus Bodin

12

파이썬 3의 경우 :

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

좋고 간결합니다. Python 2.7에서도 나를 위해 일했습니다.
donrondadon

6

for 루프 중에 변경하는 동안 사전을 반복 할 수 없습니다. 목록을 캐스팅하고 해당 목록을 반복하면 나에게 효과적입니다.

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

1

이런 상황에서는 원본 dict을 수정하는 동안 딥 카피를 만들고 해당 카피를 반복합니다.

조회 필드가 목록 내에있는 경우 목록의 for 루프에 열거 한 다음 원래 dict의 필드에 액세스하기 위해 위치를 색인으로 지정할 수 있습니다.


1

이것은 나를 위해 일했다 :

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'}  

사전 항목을 목록으로 캐스트하면 해당 항목의 목록이 작성되므로이를 반복하여 피할 수 RuntimeError있습니다.


1

dictc = { "stName": "asas"} keys = dictc.keys () 목록의 키 (키) : dictc [key.upper ()] = '새 값'print (str (dictc))


0

런타임 오류의 원인은 반복 중에 구조가 변경되는 동안 데이터 구조를 반복 할 수 없기 때문입니다.

찾고있는 것을 달성하는 한 가지 방법은 list를 사용하여 제거하려는 키를 추가 한 다음 사전에서 pop 함수를 사용하여 목록을 반복하는 동안 식별 된 키를 제거하는 것입니다.

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

파이썬 3에서는 (위의 루프를 사용하여) 사전을 반복하는 동안 삭제를 허용하지 않습니다. 해야 할 다양한 대안이 있습니다. 간단한 방법은 다음 줄을 바꾸는 것입니다.

for i in x.keys():

for i in list(x)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.