사전에서 안전하게 여러 키 제거


127

내 사전에서 'key'항목을 d안전하게 제거하는 방법을 알고 있습니다.

if d.has_key('key'):
    del d['key']

그러나 사전에서 여러 항목을 안전하게 제거해야합니다. 이 작업을 두 번 이상 수행해야하므로 튜플의 항목을 정의 할 생각이었습니다.

entitiesToREmove = ('a', 'b', 'c')
for x in entitiesToRemove:
    if d.has_key(x):
        del d[x]

그러나 이것을 수행하는 더 똑똑한 방법이 있는지 궁금합니다.


3
사전에서 검색하는 시간은 해싱 때문에 거의 O (1)입니다. 항목의 상당 부분을 제거하지 않는 한 더 잘할 수있을 것 같지 않습니다.
ncmathsadist

1
@mattbornski의 대답은 더 표준적이고 간결 해 보입니다.
요안 Filippidis

2
말에 StackOverflow의 느니라은 : key in d더 파이썬을보다 d.has_key(key) stackoverflow.com/questions/1323410/has-key-or-in
마이클 Scheper

약간의 메모리를 절약 할 수 있다면 할 수 있습니다 for x in set(d) & entities_to_remove: del d[x]. 이것은 아마도 entities_to_remove"큰" 경우에만 더 효율적일 것입니다 .
DylanYoung

답변:


56

왜 이렇게하지 :

entries = ('a', 'b', 'c')
the_dict = {'b': 'foo'}

def entries_to_remove(entries, the_dict):
    for key in entries:
        if key in the_dict:
            del the_dict[key]

보다 간결한 버전은 dict.pop ()을 사용하여 mattbornski에 의해 제공되었습니다.


14
검색 엔진에서 오는 사람들을 위해 이것을 추가합니다. (안전이 문제가되지 않을 때) 키가 알려진 경우, 여러 개의 키는 다음과 같이 한 줄에 삭제 될 수 있습니다del dict['key1'], dict['key2'], dict['key3']
Tirtha R

삭제하는 키의 수에 따라 테스트 를 사용 for key in set(the_dict) & entries:하고 우회하는 것이 더 효율적일 수 있습니다 key in dict.
DylanYoung

236
d = {'some':'data'}
entriesToRemove = ('any', 'iterable')
for k in entriesToRemove:
    d.pop(k, None)

37
이. 이것은 영리한 Pythonista의 선택입니다. dict.pop()키 존재 테스트가 필요하지 않습니다. 우수한.
Cecil Curry

4
그만한 가치가 있기 때문에 나는 .pop()나쁘고 비 파이썬 적이 라고 생각 하며 이것보다 받아 들여지는 대답을 선호합니다.
Arne

5
엄청나게 많은 사람들이 이것에 동경하지 않는 것처럼 보입니다. :) 개인적으로 존재 확인을위한 여분의 줄을 신경 쓰지 않으며, pop ()에 대해 이미 알고 있지 않는 한 훨씬 더 읽기 쉽습니다. 반면에 이해력이나 인라인 람다로 이것을하려고한다면이 트릭이 큰 도움이 될 수 있습니다. 나는 또한 그들이있는 곳에서 사람들을 만나는 것이 중요하다고 생각한다. 나는 "나쁘고 비 파이썬"이이 답변을 읽는 사람들에게 그들이 찾고있는 실용적인 지침을 줄 것이라고 확신하지 않습니다.
mattbornski

5
이것을 사용 하는 특히 좋은 이유가 있습니다. 줄을 추가하면 "가독성"또는 "명확성"이 향상 될 수 있지만 사전에 추가 조회도 추가됩니다. 이 방법은 setdefault. 올바르게 구현 된 경우 (그렇다고 확신합니다) dict두 개가 아니라 인 해시 맵을 한 번만 조회합니다 .
Mad Physicist

2
개인적으로 나는 정확성과 유지 보수성을 먼저 고려하고 속도가 불충분 한 것으로 입증 된 경우에만 속도를 고려합니다. 이러한 작업 간의 속도 차이는 응용 프로그램 수준으로 축소 할 때 사소한 것입니다. 더 빠른 경우 일 수도 있지만, 실제 사용에서는 눈치 채지도 신경 쓰지도 않을 것이며, 눈치 채고 신경 쓰면 Python보다 더 성능이 좋은 것으로 재 작성하는 것이 더 나을 것입니다.
mattbornski

89

Dict Comprehensions 사용

final_dict = {key: t[key] for key in t if key not in [key1, key2]}

여기서 key1key2 는 제거됩니다.

아래 예에서 "b"및 "c"키는 제거되고 키 목록에 보관됩니다.

>>> a
{'a': 1, 'c': 3, 'b': 2, 'd': 4}
>>> keys = ["b", "c"]
>>> print {key: a[key] for key in a if key not in keys}
{'a': 1, 'd': 4}
>>> 

4
새 사전? 목록 이해력? 질문하는 사람에게 답을 조정해야합니다.)
Glaslos

6
이 솔루션은를 보유하는 변수가 프로그램에서 더 많이 사용될 때 심각한 성능 저하를 가져옵니다. 즉, 키가 삭제 된 사전이 유지 된 항목으로 새로 생성 된 사전보다 훨씬 효율적입니다.
Apalala 2013 년

14
가독성을 위해서, I 제안을 {K : K에 대한 V, t.items에서 V () K하지의 경우 [KEY1을 키 2]}
프레드릭 Bazin

8
또한 검색 시간에 따라 키 목록이 너무 큰 경우 성능 문제가 있습니다 O(n). 전체 작업은입니다 O(mn). 여기서는 mdict n의 키 수 와 목록의 키 수입니다. {key1, key2}가능하면 대신 세트 를 사용하는 것이 좋습니다 .
ldavid

4
Apalala에게 : 성능 저하가 발생한 이유를 이해하도록 도와 주시겠습니까?

21

솔루션은 mapfilter기능을 사용 하고 있습니다.

파이썬 2

d={"a":1,"b":2,"c":3}
l=("a","b","d")
map(d.__delitem__, filter(d.__contains__,l))
print(d)

파이썬 3

d={"a":1,"b":2,"c":3}
l=("a","b","d")
list(map(d.__delitem__, filter(d.__contains__,l)))
print(d)

당신은 얻는다 :

{'c': 3}

이것은 파이썬 3.4에서 나를 위해 작동하지 않습니다 :>>> d={"a":1,"b":2,"c":3} >>> l=("a","b","d") >>> map(d.__delitem__, filter(d.__contains__,l)) <map object at 0x10579b9e8> >>> print(d) {'a': 1, 'b': 2, 'c': 3}
Risadinha

@Risadinha list(map(d.__delitem__,filter(d.__contains__,l))).... in python 3.4 map function return a iterator
Jose Ricardo Bustos M.

4
또는 deque(map(...), maxlen=0)없음 값 목록을 작성하지 않으려면; 먼저 수입과from collections import deque
제이슨

19

제거하려는 키에 대한 값도 검색해야한다면 다음과 같이하는 것이 좋습니다.

valuesRemoved = [d.pop(k, None) for k in entitiesToRemove]

물론에서 키를 제거하기 위해서만이 작업을 수행 d할 수 있지만 목록 이해를 사용하여 값 목록을 불필요하게 생성하게됩니다. 함수의 부작용만을 위해 목록 이해력을 사용하는 것도 약간 불분명합니다.


3
또는 삭제 된 항목 을 사전으로 유지하려는 경우 :valuesRemoved = dict((k, d.pop(k, None)) for k in entitiesToRemove) 등등.
kindall

변수에 대한 할당을 남겨 둘 수 있습니다. 이런 식으로 또는 그런 식으로 가장 짧고 가장 비단뱀적인 솔루션이며 corect 대답 IMHO로 표시되어야합니다.
Gerhard Hagerer 2015

12

pop및 솔루션을 찾았습니다.map

d = {'a': 'valueA', 'b': 'valueB', 'c': 'valueC', 'd': 'valueD'}
keys = ['a', 'b', 'c']
list(map(d.pop, keys))
print(d)

이것의 출력 :

{'d': 'valueD'}

나는 누군가가 똑같은 것을 검색하면 미래에 도움이 될 것이라고 생각하기 때문에이 질문에 너무 늦게 대답했습니다. 그리고 이것은 도움이 될 것입니다.

최신 정보

위 코드는 dict에 키가 없으면 오류가 발생합니다.

DICTIONARY = {'a': 'valueA', 'b': 'valueB', 'c': 'valueC', 'd': 'valueD'}
keys = ['a', 'l', 'c']

def remove_keys(key):
    try:
        DICTIONARY.pop(key, None)
    except:
        pass  # or do any action

list(map(remove_key, keys))
print(DICTIONARY)

산출:

DICTIONARY = {'b': 'valueB', 'd': 'valueD'}

1
이 대답은 키 입력 keys이 존재하지 않는 경우 예외를 throw합니다 d. 먼저이를 필터링해야합니다.
ingofreyer

@ingofreyer는 예외 처리를 위해 코드를 업데이트했습니다. 이 문제를 찾아 주셔서 감사합니다. 이제 작동 할 것이라고 생각합니다. :)
Shubham Srivastava

덕분에이 모든 사람들이 대답을 :-) 찾는 데 도움이 될 것입니다
ingofreyer

지도 사용의 부산물로 목록을 생성하면 속도가 매우 느려집니다. 실제로 반복하는 것이 좋습니다.
Charlie Clark

4

기존 답변에 문제가 없지만이 솔루션을 찾지 못한 것에 놀랐습니다.

keys_to_remove = ['a', 'b', 'c']
my_dict = {k: v for k, v in zip("a b c d e f g".split(' '), [0, 1, 2, 3, 4, 5, 6])}

for k in keys_to_remove:
    try:
        del my_dict[k]
    except KeyError:
        pass

assert my_dict == {'d': 3, 'e': 4, 'f': 5, 'g': 6}

참고 : 여기 에서이 질문을 우연히 발견 했습니다 . 그리고 내 대답은 이 대답 과 관련 있습니다.


3

왜 안 되는가 :

entriestoremove = (2,5,1)
for e in entriestoremove:
    if d.has_key(e):
        del d[e]

"스마트 한 방법"이 무슨 뜻인지 모르겠습니다. 분명히 사전 이해와 함께 다른 방법이 있습니다.

entriestoremove = (2,5,1)
newdict = {x for x in d if x not in entriestoremove}

2

인라인

import functools

#: not key(c) in d
d = {"a": "avalue", "b": "bvalue", "d": "dvalue"}

entitiesToREmove = ('a', 'b', 'c')

#: python2
map(lambda x: functools.partial(d.pop, x, None)(), entitiesToREmove)

#: python3

list(map(lambda x: functools.partial(d.pop, x, None)(), entitiesToREmove))

print(d)
# output: {'d': 'dvalue'}

2

cpython 3에 대한 일부 타이밍 테스트는 간단한 for 루프가 가장 빠른 방법이며 매우 읽기 쉽다는 것을 보여줍니다. 함수를 추가해도 많은 오버 헤드가 발생하지 않습니다.

timeit 결과 (10k 반복) :

  • all(x.pop(v) for v in r) # 0.85
  • all(map(x.pop, r)) # 0.60
  • list(map(x.pop, r)) # 0.70
  • all(map(x.__delitem__, r)) # 0.44
  • del_all(x, r) # 0.40
  • <inline for loop>(x, r) # 0.35
def del_all(mapping, to_remove):
      """Remove list of elements from mapping."""
      for key in to_remove:
          del mapping[key]

작은 반복의 경우 '인라인'을 수행하는 것이 함수 호출의 오버 헤드로 인해 조금 더 빠릅니다. 그러나 del_all보푸라기가 안전하고 재사용이 가능하며 모든 파이썬 이해 및 매핑 구조보다 빠릅니다.


0

파이썬 3을 사용하는 경우 키를 세트로 취급 할 수 있다는 사실을 사용하는 것이 가장 좋은 방법이라고 생각합니다.

def remove_keys(d, keys):
    to_remove = set(keys)
    filtered_keys = d.keys() - to_remove
    filtered_values = map(d.get, filtered_keys)
    return dict(zip(filtered_keys, filtered_values))

예:

>>> remove_keys({'k1': 1, 'k3': 3}, ['k1', 'k2'])
{'k3': 3}

0

딕셔너리에 대한 set 메소드를 완벽하게 지원하는 것이 좋을 것입니다 (파이썬 3.9에서 우리가 겪고있는 불성실 한 엉망이 아님). 그래서 단순히 키 세트를 "제거"할 수 있습니다. 그러나 그렇지 않고 제거 할 키가 많은 큰 사전이있는 한 성능에 대해 알고 싶을 수 있습니다. 그래서 저는 의미있는 비교를 위해 충분히 큰 무언가를 만드는 코드를 만들었습니다. 100,000 x 1000 행렬이므로 총 10,000,00 개의 항목이 있습니다.

from itertools import product
from time import perf_counter

# make a complete worksheet 100000 * 1000
start = perf_counter()
prod = product(range(1, 100000), range(1, 1000))
cells = {(x,y):x for x,y in prod}
print(len(cells))

print(f"Create time {perf_counter()-start:.2f}s")
clock = perf_counter()
# remove everything above row 50,000

keys = product(range(50000, 100000), range(1, 100))

# for x,y in keys:
#     del cells[x, y]

for n in map(cells.pop, keys):
    pass

print(len(cells))
stop = perf_counter()
print(f"Removal time {stop-clock:.2f}s")

일부 설정에서는 천만 개 이상의 항목이 드물지 않습니다. 내 로컬 컴퓨터에서 두 가지 방법을 비교하면 mapand를 사용할 때 약간의 개선을 보았습니다. pop아마도 함수 호출이 적기 때문일 것입니다.하지만 둘 다 내 컴퓨터에서 약 2.5 초가 걸립니다. 그러나 이것은 처음에 사전을 만드는 데 필요한 시간 (55 초) 또는 루프 내에 수표를 포함하는 데 필요한 시간과 비교하면 부족합니다. 이것이 가능하다면 사전 키와 필터가 교차하는 집합을 만드는 것이 가장 좋습니다.

keys = cells.keys() & keys

요약하면 : del이미 많이 최적화되어 있으므로 사용에 대해 걱정하지 마십시오.


-1

나는이 토론에 늦었지만 다른 사람에게는 늦었습니다. 해결책은 키 목록을 만드는 것입니다.

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

그런 다음 목록 이해 또는 for 루프에서 pop ()을 사용하여 키를 반복하고 한 번에 하나씩 팝합니다.

new_dictionary = [dictionary.pop(x, 'n/a') for x in k]

'해당 없음'은 키가없는 경우 기본값을 반환해야합니다.


8
new_dictionary목록처럼 엄청 많이 보인다)
DylanYoung
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.