사전을 반복하면서 사전에서 항목을 삭제하는 방법은 무엇입니까?


295

반복하면서 파이썬의 사전에서 항목을 삭제하는 것이 합법적입니까?

예를 들면 다음과 같습니다.

for k, v in mydict.iteritems():
   if k == val:
     del mydict[k]

아이디어는 반복되는 사전의 하위 세트 인 새 사전을 작성하는 대신 사전에서 특정 조건을 충족하지 않는 요소를 제거하는 것입니다.

이것이 좋은 해결책입니까? 더 우아하고 효율적인 방법이 있습니까?


1
매우 흥미로운 답변 이와 관련된 질문 : stackoverflow.com/questions/9023078/... .
최대

하나는 쉽게 시도 할 수있었습니다. 실패하면 합법적이지 않습니다.
Trilarion

26
@Trilarion One은 쉽게 시도 할 수 있었으며 쉽게 가치를 배울 수 없었습니다. 성공하면 반드시 합법적 인 것은 아닙니다. 에지 사례와 예상치 못한 경고가 많이 발생합니다. 이 질문은 모든 Pythonistas에게 사소한 관심사입니다. "하나는 쉽게 시도 할 수있었습니다!" 도움이되지 않고 호기심 많은 스택 오버 플로우 문의 정신에 위배됩니다.
Cecil Curry

정독 후 최대 것은관련 질문은 , 내가 동의해야합니다. 당신은 아마도 그 혼란스럽고 심층적 인 질문과 잘 쓰여진 답을 대신하고 싶을 것입니다. 파이썬 마음이 날 것입니다.
Cecil Curry

1
@CecilCurry 여기에 제시하기 전에 스스로 아이디어를 테스트하는 것은 내가 실수하지 않으면 일종의 stackoverflow의 정신에있다. 그것이 내가 전하고 싶은 전부였습니다. 그로 인해 방해가 된 경우 죄송합니다. 또한 나는 그것이 좋은 질문이라고 생각하고 그것을 공감하지 않았다. 나는 Jochen Ritzel 의 답변을 가장 좋아합니다 . 두 번째 단계에서 삭제하는 것이 훨씬 간단하다는 것을 즉시 삭제해야 할 필요는 없다고 생각합니다. 그것은 내 관점에서 선호하는 방법이어야합니다.
Trilarion

답변:


305

편집하다:

이 답변은 Python3에서는 작동하지 않으며을 제공합니다 RuntimeError.

RuntimeError : 반복하는 동안 사전 크기가 변경되었습니다.

mydict.keys()목록이 아닌 반복자를 반환 하기 때문에 발생 합니다. 의견에서 지적했듯이 간단히 mydict.keys()목록으로 변환 하면 list(mydict.keys())작동합니다.


콘솔의 간단한 테스트는 사전을 반복하는 동안 사전을 수정할 수 없음을 보여줍니다.

>>> mydict = {'one': 1, 'two': 2, 'three': 3, 'four': 4}
>>> for k, v in mydict.iteritems():
...    if k == 'two':
...        del mydict[k]
...
------------------------------------------------------------
Traceback (most recent call last):
  File "<ipython console>", line 1, in <module>
RuntimeError: dictionary changed size during iteration

delnan의 답변에서 언급했듯이 항목을 삭제하면 반복자가 다음 항목으로 이동하려고 할 때 문제가 발생합니다. 대신, keys()메소드를 사용하여 키 목록을 가져 와서 작업하십시오.

>>> for k in mydict.keys():
...    if k == 'two':
...        del mydict[k]
...
>>> mydict
{'four': 4, 'three': 3, 'one': 1}

items 값을 기준으로 삭제 해야하는 경우 items()대신 메소드를 사용하십시오 .

>>> for k, v in mydict.items():
...     if v == 3:
...         del mydict[k]
...
>>> mydict
{'four': 4, 'one': 1}

53
파이썬 3에서 dict.items ()는 반복자를 반환합니다 (그리고 dict.iteritems ()는 사라졌습니다).
Tim Lesher

83
@TimLesher 의견을 자세히 설명하려면 ... 이것은 Python 3에서는 작동하지 않습니다.
max

99
@max의 정교함을 정교하게하기 위해 위의 코드를 2to3으로 변환하면 효과가 있습니다. 기본 정착액 중 하나는 같은 루프 보이게 for k, v in list(mydict.items()):에 대한 같은 파이썬 3에서 잘 작동 keys()되는가 list(keys()).
Walter Mundt

8
작동하지 않습니다. 오류가 발생했습니다 :RuntimeError: dictionary changed size during iteration
Tomáš Zato-Reinstate Monica

14
Walter가 지적한 @ TomášZato, python3의 경우 python3을 사용 for k in list(mydict.keys()): 하여 keys () 메서드를 반복자로 만들고 반복하는 동안 dict 항목을 삭제할 수 없습니다. list () 호출을 추가하여 keys () 반복자를 목록으로 바꿉니다. 따라서 for 루프 본문에 있으면 더 이상 사전 자체를 반복하지 않습니다.
Geoff Crompton

89

두 단계로 수행 할 수도 있습니다.

remove = [k for k in mydict if k == val]
for k in remove: del mydict[k]

내가 가장 좋아하는 접근법은 일반적으로 새로운 전략을 만드는 것입니다.

# Python 2.7 and 3.x
mydict = { k:v for k,v in mydict.items() if k!=val }
# before Python 2.7
mydict = dict((k,v) for k,v in mydict.iteritems() if k!=val)

11
@ senderle : 실제로 2.7부터.
Jochen Ritzel

5
dict comprehension 접근법은 사전의 사본을 만듭니다. 운 좋게도 값은 깊게 복사되지 않고 연결되어 있습니다. 그래도 키가 많으면 나쁠 수 있습니다. 이런 이유로 remove루프 접근 방식이 더 좋습니다.
최대

1
단계를 결합 할 수도 있습니다.for k in [k for k in mydict if k == val]: del mydict[k]
AXO

첫 번째 솔루션은 전체 스레드를 만들지 않기 때문에이 스레드에서 큰 dicts에 대한 유일한 효율적인 솔루션입니다.
kxr

21

반복하는 동안 컬렉션을 수정할 수 없습니다. 그 방법은 광기입니다-가장 눈에 띄게, 현재 항목을 삭제하고 삭제하도록 허용 된 경우 반복자가 (+1) 이동해야하고 다음 호출 next은 그 이상으로 넘어갈 것입니다 (+2). 한 요소 (삭제 한 요소 바로 뒤에있는 요소)를 건너 뜁니다. 두 가지 옵션이 있습니다.

  • 모든 키 (또는 필요한 것에 따라 값 또는 둘 다)를 복사 한 다음 반복하십시오. .keys()이것을 위해 et al을 사용할 수 있습니다 (Python 3에서는 결과 반복자를에 전달하십시오 list). 공간적으로 많은 낭비가 될 수 있습니다.
  • mydict평소와 같이 반복 하여 별도의 컬렉션에서 삭제할 키를 저장합니다 to_delete. 당신이 반복하는 일을 할 때 mydict, 모든 항목을 삭제 to_delete에서 mydict. 첫 번째 접근 방식에서 일부 (삭제 된 키 수 및 유지 수에 따라) 공간을 절약 할 수 있지만 몇 줄이 더 필요합니다.

You can't modify a collection while iterating it.이 dicts 및 친구를위한 단지 정확하지만 반복하는 동안 목록을 수정할 수 있습니다 :L = [1,2,None,4,5] <\n> for n,x in enumerate(L): <\n\t> if x is None: del L[n]
닐스 린데

3
@Nils 예외는 발생하지 않지만 여전히 올바르지 않습니다. 관찰 : codepad.org/Yz7rjDVT- 설명 은 예를 들어 stackoverflow.com/q/6260089/395760 참조

여기로 여전히 can'tdict과 친구들에게만 올바른 반면 shouldn't목록에는 적합합니다.
Nils Lindemann

20

대신에 반환 된 것과 같은 사본을 반복하십시오 items().

for k, v in list(mydict.items()):

1
그다지 의미가 없습니다. 그러면 del v직접 사용할 수 없으므로 사용하지 않을 각 v의 사본을 만들었으므로 어쨌든 키로 항목에 액세스해야합니다. dict.keys()더 나은 선택입니다.
jscs

2
@ 조쉬 : 그것은 모두 당신이 v삭제 기준 으로 얼마나 많이 사용해야 할 지에 달려 있습니다.
Ignacio Vazquez-Abrams

3
Python 3에서는 dict.items()복사본이 아닌 반복자를 반환합니다. 블레어답변에 대해서는 해설을 참조하십시오. 슬프게도 파이썬 2 의미론을 가정합니다.
Cecil Curry

10

사용하는 것이 가장 깨끗합니다 list(mydict).

>>> mydict = {'one': 1, 'two': 2, 'three': 3, 'four': 4}
>>> for k in list(mydict):
...     if k == 'three':
...         del mydict[k]
... 
>>> mydict
{'four': 4, 'two': 2, 'one': 1}

이것은 목록의 병렬 구조에 해당합니다.

>>> mylist = ['one', 'two', 'three', 'four']
>>> for k in list(mylist):                            # or mylist[:]
...     if k == 'three':
...         mylist.remove(k)
... 
>>> mylist
['one', 'two', 'four']

둘 다 python2와 python3에서 작동합니다.


데이터 세트가 큰 경우에는 좋지 않습니다. 이것은 메모리의 모든 객체를 복사하는 것입니다.
AFP_555

1
@ AFP_555 예-여기의 목표는 깨끗하고 평행 한 파이썬 코드입니다. 메모리 효율성이 필요한 경우 내가 아는 가장 좋은 방법은 삭제할 키 목록이나 저장할 새 항목을 반복하여 작성하는 것입니다. 아름다움은 파이썬의 우선 순위입니다. 큰 데이터 세트의 경우 Go 또는 Rust를 사용하고 있습니다.
rsanden

9

사전 이해력을 사용할 수 있습니다.

d = {k:d[k] for k in d if d[k] != val}


이것은 가장 Pythonic입니다.
Yehosef

그러나 수정하지 않고 새 사전을 만듭니다 d.
Aristide

9

python3을 사용하면 dic.keys ()를 반복하면 사전 크기 오류가 발생합니다. 이 대체 방법을 사용할 수 있습니다.

python3으로 테스트 한 결과 제대로 작동하고 " 반복 중 크기가 변경된 사전 "오류 가 발생하지 않습니다.

my_dic = { 1:10, 2:20, 3:30 }
# Is important here to cast because ".keys()" method returns a dict_keys object.
key_list = list( my_dic.keys() )

# Iterate on the list:
for k in key_list:
    print(key_list)
    print(my_dic)
    del( my_dic[k] )


print( my_dic )
# {}

4

먼저 삭제할 키 목록을 작성한 다음 해당 목록을 반복하여 삭제할 수 있습니다.

dict = {'one' : 1, 'two' : 2, 'three' : 3, 'four' : 4}
delete = []
for k,v in dict.items():
    if v%2 == 1:
        delete.append(k)
for i in delete:
    del dict[i]

오히려 @Ritzel의 첫 번째 솔루션 (복사본이없는 큰 사전에 효율적)의 복제본입니다. 목록 이해력이없는 "긴 읽기"이지만. 그럼에도 불구하고 아마도 더 빠를까요?
kxr

3

삭제하려는 항목이 항상 dict 반복의 "시작"에있는 경우 적합한 방법이 있습니다

while mydict:
    key, value = next(iter(mydict.items()))
    if should_delete(key, value):
       del mydict[key]
    else:
       break

"시작"은 특정 Python 버전 / 구현에 대해서만 일관성이 보장됩니다. 예를 들어 Python 3.7의 새로운 기능

dict 객체의 삽입 순서 보존 특성은 Python 언어 사양의 공식 부분으로 선언되었습니다.

이 방법은 적어도 파이썬 3에서 많은 다른 답변이 제안하는 dict 사본을 피합니다.


1

Python3에서 위의 솔루션을 시도했지만이 객체를 dict에 객체를 저장할 때 유일하게 작동하는 것 같습니다. 기본적으로 dict () 사본을 만들고 원래 사전의 항목을 삭제하는 동안 반복합니다.

        tmpDict = realDict.copy()
        for key, value in tmpDict.items():
            if value:
                del(realDict[key])
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.