Удалить элемент из словаря, если его ключ неизвестен

112

Как лучше всего удалить элемент из словаря по значению, т.е. когда ключ элемента неизвестен? Вот простой подход:

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

Есть способы лучше? Что-то не так с изменением (удалением элементов) из словаря при его повторении?

Buttons840
источник
1
Подчеркнутая причина запрета на изменение dict во время его итерации заключается в том, что внутри существует порядок итерации, если вы измените ключи, порядок будет нарушен, что приведет к неизвестному поведению.
Spectral
Возможный дубликат Как удалить ключ из словаря Python?
tripleee

Ответы:

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
Это сжатие словаря? Когда они были добавлены?
Buttons840
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 или отображением словаря . поскольку PEP говорит, что они были добавлены в 2.7 как backported возможности 3.x. в качестве альтернативы вы можете dict()использовать соответствующее выражение генератора, равное 2.4. meta: можете просмотреть здесь, чтобы узнать что- нибудь .
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}
№ 1.1
источник
4
OP спросил, когда ключ неизвестен
nmz787
52
a = {'name': 'your_name','class': 4}
if 'name' in a: del a['name']
Kracekumar
источник
OP спросил, когда ключ неизвестен. Этот ответ предполагает, что ключ известен.
Жан-Франсуа Корбетт
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оператора.
Alex Dupuy
1
Вспомогательный к вопросу, но я также изо всех сил пытался понять timeit. Спасибо за этот наглядный пример.
Adam_G 01
OP спросил, когда ключ неизвестен. Этот ответ предполагает, что ключ известен.
Жан-Франсуа Корбетт
7

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

Я не могу придумать лучшего способа удалить элементы из dict по значению.

mithrandi
источник
7

Я бы составил список ключей, которые нужно удалить, а затем удалил бы их. Это просто, эффективно и позволяет избежать проблем с одновременным повторением и изменением dict.

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 спросил, когда ключ неизвестен. Этот ответ предполагает, что ключ известен.
Жан-Франсуа Корбетт
1
y={'username':'admin','machine':['a','b','c']}
if 'c' in y['machine'] : del y['machine'][y['machine'].index('c')]
пользователь3559640
источник
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. См. Объяснение Митранди.
Buttons840,
1
Конечно, d.iteritems () - это не то, как повторяется исходный плакат, и не то, что я имел в виду в своем ответе.
Thane Anthem
0

Вот как бы я это сделал.

for key in some_dict.keys():
    if some_dict[key] == item_to_remove:
        some_dict.pop(key)
        break
Натан
источник