Usuń element ze słownika, gdy jego klucz jest nieznany

112

Jaki jest najlepszy sposób usunięcia pozycji ze słownika według wartości, tj. Gdy klucz pozycji jest nieznany? Oto proste podejście:

for key, item in some_dict.items():
    if item is item_to_remove:
        del some_dict[key]

Czy są lepsze sposoby? Czy jest coś złego w mutowaniu (usuwaniu elementów) ze słownika podczas iteracji?

Przyciski840
źródło
1
Podkreślony powód zakazu mutowania dyktowania podczas iteracji jest taki, że wewnętrznie istnieje kolejność iteracji, jeśli zmutujesz klucze, kolejność zostanie podważona, co spowoduje nieznane zachowanie.
Spectral
Możliwy duplikat Jak usunąć klucz ze słownika Pythona?
tripleee

Odpowiedzi:

92

Należy pamiętać, że aktualnie testujesz tożsamość obiektu ( iszwraca tylko Truewtedy, gdy oba operandy są reprezentowane przez ten sam obiekt w pamięci - nie zawsze tak jest w przypadku dwóch obiektów, które porównują równe z ==). Jeśli robisz to celowo, możesz przepisać swój kod jako

some_dict = {key: value for key, value in some_dict.items() 
             if value is not value_to_remove}

Ale to może nie zrobić tego, co chcesz:

>>> some_dict = {1: "Hello", 2: "Goodbye", 3: "You say yes", 4: "I say no"}
>>> value_to_remove = "You say yes"
>>> some_dict = {key: value for key, value in some_dict.items() if value is not value_to_remove}
>>> some_dict
{1: 'Hello', 2: 'Goodbye', 3: 'You say yes', 4: 'I say no'}
>>> some_dict = {key: value for key, value in some_dict.items() if value != value_to_remove}
>>> some_dict
{1: 'Hello', 2: 'Goodbye', 4: 'I say no'}

Więc prawdopodobnie chcesz !=zamiast is not.

Tim Pietzcker
źródło
2
Czy to kompresja słownika? Kiedy zostały dodane?
Przyciski 840
4
możesz użyć some_dict.iteritems()tutaj i umieścić fori ifoświadczenia w oddzielnych wierszach dla czytelności
jfs
3
Myślę, że wyrażenia słownikowe zostały dodane w Pythonie 2.7.
mithrandi
2
@JF Sebastian: Jestem na Pythonie 3 i jestem iteritemsteraz items. W Pythonie 2.7 iteritems()jest rzeczywiście lepszy.
Tim Pietzcker,
1
@ Przyciski840 w PEP 274 lub w słownikach nazywane są one wyrażeniami dict . jak mówi PEP, zostały dodane w 2.7 jako backportowane wyczyny 3.x. alternatywnie możesz zasilić dict()odpowiednim wyrażeniem generatora, czyli 2.4. meta: może przeglądać peps tutaj, aby znaleźć rzeczy.
n611x007
120

dict.pop(key[, default])Metoda pozwala na usuwanie elementów, gdy wiesz klucz. Zwraca wartość z klucza, jeśli usuwa element, w przeciwnym razie zwraca wartość przekazaną jako default. Zobacz dokumentację .

Przykład:

>>> dic = {'a':1, 'b':2}
>>> dic
{'a': 1, 'b': 2}
>>> dic.pop('c', 0)
0
>>> dic.pop('a', 0)
1
>>> dic
{'b': 2}
N 1.1
źródło
4
OP zapytał, kiedy klucz jest nieznany
nmz787
52
a = {'name': 'your_name','class': 4}
if 'name' in a: del a['name']
Kracekumar
źródło
OP zapytał, kiedy klucz jest nieznany. Ta odpowiedź zakłada, że ​​klucz jest znany.
Jean-François Corbett
42

Proste porównanie między del i pop () :

import timeit
code = """
results = {'A': 1, 'B': 2, 'C': 3}
del results['A']
del results['B']
"""
print timeit.timeit(code, number=100000)
code = """
results = {'A': 1, 'B': 2, 'C': 3}
results.pop('A')
results.pop('B')
"""
print timeit.timeit(code, number=100000)

wynik:

0.0329667857143
0.0451040902256

Tak więc del jest szybsze niż pop () .

Luu Tuan Anh
źródło
6
Jednak różnica w wydajności nie jest duża i jeśli chcesz uniknąć zgłaszania wyjątku, możesz podać drugi argument pop()(jak w przypadku @ n-1-1 powyżej) - co nie jest opcją dla deloperatora.
Alex Dupuy,
1
Pomocniczy w stosunku do pytania, ale też starałem się zrozumieć timeit. Dziękuję za ten jasny przykład.
Adam_G
OP zapytał, kiedy klucz jest nieznany. Ta odpowiedź zakłada, że ​​klucz jest znany.
Jean-François Corbett
7

items()zwraca listę i to jest ta lista, którą iterujesz, więc mutowanie dyktowania w pętli nie ma tutaj znaczenia. Gdybyś iteritems()zamiast tego używał , mutowanie dyktowania w pętli byłoby problematyczne , podobnie jak viewitems()w Pythonie 2.7.

Nie mogę wymyślić lepszego sposobu na usuwanie pozycji z dyktu według wartości.

mithrandi
źródło
7

Zbudowałbym listę kluczy, które trzeba usunąć, a następnie je usunąłem. Jest to proste, wydajne i pozwala uniknąć problemów związanych z jednoczesnym iterowaniem i mutowaniem dyktowania.

keys_to_remove = [key for key, value in some_dict.iteritems()
                  if value == value_to_remove]
for key in keys_to_remove:
    del some_dict[key]

źródło
OP zapytał, kiedy klucz jest nieznany. Ta odpowiedź zakłada, że ​​klucz jest znany.
Jean-François Corbett
1
y={'username':'admin','machine':['a','b','c']}
if 'c' in y['machine'] : del y['machine'][y['machine'].index('c')]
user3559640
źródło
0

Nie ma nic złego w usuwaniu elementów ze słownika podczas iteracji, jak zaproponowałeś. Uważaj na wiele wątków używających tego samego słownika w tym samym czasie, co może spowodować błąd KeyError lub inne problemy.

Oczywiście zapoznaj się z dokumentacją pod adresem http://docs.python.org/library/stdtypes.html#typesmapping

Thane Anthem
źródło
for k,v in d.iteritems(): del d[k]dałoby RuntimeError: dictionary changed size during iteration. Zobacz wyjaśnienie mithrandi.
Przyciski 840
1
Oczywiście d.iteritems () nie jest sposobem iteracji oryginalnego plakatu, a nie tym, o czym mówiłem w mojej odpowiedzi.
Thane Anthem
0

Tak bym to zrobił.

for key in some_dict.keys():
    if some_dict[key] == item_to_remove:
        some_dict.pop(key)
        break
Nathan
źródło