Видаліть елемент зі словника, коли його ключ невідомий


112

Який найкращий спосіб видалити елемент зі словника за значенням, тобто коли ключ елемента невідомий? Ось простий підхід:

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

Чи є кращі способи? Чи є щось не так у мутації (видаленні елементів) зі словника під час його повторення?


1
Основна причина заборони мутувати дікт під час його повторення полягає в тому, що внутрішньо існує порядок ітерації, якщо ви мутуєте клавіші, порядок буде підірваний, що призводить до невідомої поведінки.
Спектрал

Відповіді:


92

Майте на увазі, що ви зараз тестуєте ідентичність об'єкта ( isповертається лише в тому Trueвипадку, якщо обидва операнди представлені одним і тим же об’єктом у пам'яті - це не завжди так, якщо два об'єкти порівнюються з рівними ==). Якщо ви робите це навмисно, ви можете переписати свій код як

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

Тож ви, мабуть, хочете !=замість цього is not.


2
Це стиснення словника? Коли їх додавали?
Кнопки840

4
ви можете використати some_dict.iteritems()тут і поставити forі ifзаяви в окремі рядки для читабельності
jfs

3
Я вважаю, що розуміння словника було додано в Python 2.7.
mithrandi

2
@JF Себастьян: Я на Python 3, і iteritemsзараз items. У Python 2.7 iteritems()дійсно краще.
Тім Піцкер

1
@ Buttons840 їх називають розумінням диктату в PEP 274 або відображенні словника . як каже ПЕП, вони додавались у 2,7 як підтримувані 3-кратні подвиги. Ви також можете подавати dict()відповідний вираз генератора, який становить 2,4. мета: тут можна переглянути перегляди, щоб знайти речі.
n611x007

120

dict.pop(key[, default])Метод дозволяє видаляти елементи , коли ви знаєте , ключ. Він повертає значення за ключем, якщо він вилучає елемент, інакше він повертає те, що передається як default. Дивіться документи . '

Приклад:

>>> dic = {'a':1, 'b':2}
>>> dic
{'a': 1, 'b': 2}
>>> dic.pop('c', 0)
0
>>> dic.pop('a', 0)
1
>>> dic
{'b': 2}

4
ОП запитало про те, коли ключ невідомий
nmz787


42

Просте порівняння між del і 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)

результат:

0.0329667857143
0.0451040902256

Отже, del швидше, ніж pop () .


6
Однак різниця в продуктивності не велика, і якщо ви хочете уникнути підвищення винятку, ви можете надати другий аргумент pop()(як @ n-1-1 робить вище) - що не є опцією для delоператора.
Алекс Дюпюй

1
Допоміжне питання, але я також намагався зрозуміти timeit. Дякую за цей наочний приклад.
Adam_G

ОП запитав щодо того, коли ключ невідомий. Ця відповідь передбачає, що ключ відомий.
Жан-Франсуа Корбетт

7

items()повертає список, і це той список, який ви ітератуєте, тому вимкнення дикта в циклі тут не має значення. Якщо ви використовували iteritems()натомість, вимкнення дикта в циклі було б проблематичним , як і viewitems()в Python 2.7.

Я не можу придумати кращий спосіб видалення предметів із диктату за значенням.


7

Я буду складати список ключів, які потрібно видалити, а потім видалити їх. Це просто, ефективно та дозволяє уникнути будь-якої проблеми з одночасним перетворенням та вимкненням диктату.

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]

ОП запитав щодо того, коли ключ невідомий. Ця відповідь передбачає, що ключ відомий.
Жан-Франсуа Корбетт



0

Немає нічого поганого в тому, що під час ітерації вилучаєте елементи зі словника, як ви запропонували. Будьте уважні до кількох потоків, використовуючи один і той же словник одночасно, що може спричинити за собою KeyError або інші проблеми.

Звичайно, дивіться документи на http://docs.python.org/library/stdtypes.html#typesmapping


for k,v in d.iteritems(): del d[k]дав би RuntimeError: dictionary changed size during iteration. Див. Пояснення мітранді.
Кнопки840

1
Звичайно, d.iteritems () - це не те, як оригінальний плакат повторюється, а не те, про що я мав на увазі у своїй відповіді.
Гімн Тана

0

Ось як я це зробив би.

for key in some_dict.keys():
    if some_dict[key] == item_to_remove:
        some_dict.pop(key)
        break
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.