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?


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

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.


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}

4
OP zapytał, kiedy klucz jest nieznany
nmz787


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 () .


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.


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]

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')]

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


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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.