Удалить элемент из словаря

1396

Есть ли способ удалить элемент из словаря в Python?

Кроме того, как я могу удалить элемент из словаря, чтобы вернуть копию (то есть, не изменяя оригинал)?

richzilla
источник
13
Зачем вам нужна функция, которая возвращает словарь, когда вы можете просто изменить словарь напрямую?
amillerrhodes
5
Словарь popметод изменяет словарь на месте . Поэтому он изменяет ссылку на словарь, который был передан от вызывающей стороны к «вспомогательной функции». Таким образом, «вспомогательная функция» не должна ничего возвращать, так как исходная ссылка на словарь в вызывающей программе уже будет изменена. Не назначайте возврат dict.pop()ни к чему, если он вам не нужен. EG: do stuff with my_dict; my_dict.pop(my_key, None); do more stuff with my_dict # now doesn't have my_key. Используйте deepcopy(my_dict)при необходимости.
Марк Микофски
1
Поскольку первоначальное название не соответствовало деталям и конкретно исключало очевидное решение d.pop(), я исправил название, чтобы задать вопрос, указанный в деталях.
smci
1
Мы должны добавить предостережение, спрашивающее, действительно ли вы хотите это сделать, как если бы вы делали это N раз в словаре с элементами E, у вас будет утечка (/ использование) O (N * E) памяти со всеми глубокими копиями. Если вы просто хотите только для чтения (мелкая копия), сделайте d.pop(key). Но если что-либо изменяет мелкую копию, у вас есть хорошо известная проблема с алиасами . Это поможет, если вы сообщите нам более широкий контекст. (Изменяет ли что-нибудь еще значения dict? Вы пытаетесь деструктивно перебирать список? Если нет, то что?)
smci
5
«Зачем вам нужна функция, которая возвращает словарь, когда вы можете просто изменить словарь напрямую?» Возможно, потому что вы хотите написать чистые функции, которые не изменяют свои параметры?
Джин Каллахан

Ответы:

1729

delОператор удаляет элемент:

del d[key]

Однако это изменяет существующий словарь, поэтому содержимое словаря изменяется для всех, кто имеет ссылку на тот же экземпляр. Чтобы вернуть новый словарь, сделайте копию словаря:

def removekey(d, key):
    r = dict(d)
    del r[key]
    return r

dict()Конструктор делает неполную копию . Чтобы сделать глубокую копию, см. copyМодуль .


Обратите внимание, что создание копии для каждого диктанта del/ назначения / и т. Д. означает, что вы переходите от постоянного времени к линейному времени, а также используете линейное пространство. Для маленьких диктовок это не проблема. Но если вы планируете делать много копий больших диктовок, вам, вероятно, понадобится другая структура данных, например HAMT (как описано в этом ответе ).

Грег Хьюгилл
источник
15
это замечательный момент в отношении изменчивости словарей +1 - хотя я не могу вспомнить время, когда я хотел получить копии словаря, я всегда полагался на то, что копия «всех» одинакова. отличный момент.
ТМС
30
@tMC Если вы отредактируете dictцикл во время его прохождения, он выдаст ошибку:RuntimeError: dictionary changed size during iteration
VertigoRay
15
Как насчет popметода, который на самом деле делает то же самое? Разве это не более питонично? (будучи методом dict, а не специальным зарезервированным словом)?
Серж
21
Этот ответ имеет слабость, он может вводить в заблуждение. Читатели могут неправильно понять, что dict (d) может дать им копию с «d». Но это неполная копия. Когда выполняются только операции с ключами, это нормально. Но когда вы хотите сделать что-то еще с вложенным диктом, изменение 'r' с помощью этого метода копирования может привести к изменению исходного 'd'. Чтобы получить подлинную копию, вам сначала нужно «импортировать копию», а затем «r = copy.deepcopy (d)».
Дзен
10
@Zen: Достаточно справедливо, я добавил примечание о мелком и глубоком копировании.
Грег Хьюгилл
264

pop мутирует словарь.

 >>> lol = {"hello": "gdbye"}
 >>> lol.pop("hello")
     'gdbye'
 >>> lol
     {}

Если вы хотите сохранить оригинал, вы можете просто скопировать его.

кристалл
источник
29
«del» - это нормально, но «pop» кажется более «питоническим», на мой взгляд.
ivanleoncz
1
@ivanleoncz почему?
Кевр
3
popвозвращает значение, которое было «вытолкнуто», что позволяет вам использовать это значение по любой другой причине. Если это не более "Pythonic", я бы сказал, что, кажется, лучше :). Это не диктат, но он работает одинаково для обоих: github.com/ivanlmj/python-prototypes/blob/master/3.4/…
ivanleoncz
2
@ivanleoncz Это также лучше по еще одной причине, popможет быть предоставлено значение по умолчанию, которое будет возвращено, если ключ отсутствует в dict. Хорошо, когда вам нужно удалить несколько ключей, но некоторые из них могут отсутствовать; delбросил бы KeyErrorв таком случае.
Итачи
80

Я думаю, что ваше решение - лучший способ сделать это. Но если вам нужно другое решение, вы можете создать новый словарь, используя ключи из старого словаря, не включая указанный вами ключ, например так:

>>> a
{0: 'zero', 1: 'one', 2: 'two', 3: 'three'}
>>> {i:a[i] for i in a if i!=0}
{1: 'one', 2: 'two', 3: 'three'}
utdemir
источник
2
Очень круто. Мне нравится быстрый метод фильтрации словаря без определения новой функции.
Джо Дж
6
Для тех, кто не знаком с пониманием, вы также можете сделать что-то вроде этого: {i:a[i] for i in a if i not in [0, 1, 2]}если вы хотите удалить несколько элементов.
kmatheny
9
{k:v for k,v in a.items() if k != 0}Я думаю, лучше было бы .
rlbond
1
Лучшее решение для удаления элемента по ключу и возврата результата нового dict в той же строке. Например, если вам нужно использовать уже построенный дикт без единого элемента, как **kwargs,some_function(**{k:v for k,v in some_dict.items() if k not 'some_key'})
Коул
Лучшее решение здесь. Один лайнер, и он не изменяет оригинальный словарь.
Эндрю Винтерботам
55

Оператор del - это то, что вы ищете. Если у вас есть словарь с именем foo с ключом «bar», вы можете удалить «bar» из foo следующим образом:

del foo['bar']

Обратите внимание, что это постоянно изменяет используемый словарь. Если вы хотите сохранить исходный словарь, вам придется создать копию заранее:

>>> foo = {'bar': 'baz'}
>>> fu = dict(foo)
>>> del foo['bar']
>>> print foo
{}
>>> print fu
{'bar': 'baz'}

dictВызов делает неполную копию. Если вы хотите глубокую копию, используйте copy.deepcopy.

Вот метод, который вы можете скопировать и вставить для вашего удобства:

def minus_key(key, dictionary):
    shallow_copy = dict(dictionary)
    del shallow_copy[key]
    return shallow_copy
arussell84
источник
1
@ pythonian29033, вообще-то нет . Принятый ответ работает как положено - он возвращает дикт без единого ключа. Подход из этого ответа мутирует оригинальный дикт;) Есть существенная разница
maxkoryukov
1
@ arussell84, почему >>>часто используется в python-примерах? Да, Python-Doc содержит много таких вещей. Но такой код неудобен для копирования . Я в замешательстве ...
Макскорюков
@maxkoryukov Да, это так! но эта функция и этот ответ абсолютно одинаковы, за исключением того, что этот ответ находится внутри функции. и вы, должно быть, некоторое время не программировали >>>на python,
имитируя
2
@ pythonian29033 о >>>. Да, это стиль REPL, но давайте поговорим откровенно: только один человек написал этот образец, и 1000 читали это. Я думаю, было бы здорово написать примеры так, чтобы можно было легко копировать и запускать. Я не люблю снимать эти угловые скобки вручную. Или копировать построчно .. Так что я не понимаю: почему эти углы все еще существуют))) Может быть, я чего-то не знаю?
Макскорюков
3
Для вашего удобства я добавил функцию, которую можно копировать / вставлять.
arussell84
48

Есть много хороших ответов, но я хочу подчеркнуть одну вещь.

Вы можете использовать как dict.pop()метод, так и более общий delоператор для удаления элементов из словаря. Они оба видоизменяют оригинальный словарь, поэтому вам нужно сделать копию (см. Подробности ниже).

И они оба выдают a, KeyErrorесли ключ, который вы им предоставляете, отсутствует в словаре:

key_to_remove = "c"
d = {"a": 1, "b": 2}
del d[key_to_remove]  # Raises `KeyError: 'c'`

а также

key_to_remove = "c"
d = {"a": 1, "b": 2}
d.pop(key_to_remove)  # Raises `KeyError: 'c'`

Вы должны позаботиться об этом:

захватывая исключение:

key_to_remove = "c"
d = {"a": 1, "b": 2}
try:
    del d[key_to_remove]
except KeyError as ex:
    print("No such key: '%s'" % ex.message)

а также

key_to_remove = "c"
d = {"a": 1, "b": 2}
try:
    d.pop(key_to_remove)
except KeyError as ex:
    print("No such key: '%s'" % ex.message)

выполнив проверку:

key_to_remove = "c"
d = {"a": 1, "b": 2}
if key_to_remove in d:
    del d[key_to_remove]

а также

key_to_remove = "c"
d = {"a": 1, "b": 2}
if key_to_remove in d:
    d.pop(key_to_remove)

но pop()есть и более краткий способ - укажите возвращаемое значение по умолчанию:

key_to_remove = "c"
d = {"a": 1, "b": 2}
d.pop(key_to_remove, None)  # No `KeyError` here

Если вы не используете pop()для получения значения удаляемого ключа, вы можете предоставить все, что не нужно None. Хотя может случиться так, что использование delс inпроверкой немного быстрее из-за того, pop()что это функция со своими собственными сложностями, вызывающими накладные расходы. Обычно это не так, поэтому pop()значение по умолчанию вполне достаточно.


Что касается основного вопроса, вам нужно будет сделать копию своего словаря, чтобы сохранить исходный словарь и получить новый без удаления ключа.

Некоторые другие люди здесь предлагают сделать полную (глубокую) копию с copy.deepcopy(), что может быть избыточным, «нормальной» (мелкой) копией, с использованием copy.copy()или dict.copy()может быть достаточно. Словарь хранит ссылку на объект в качестве значения для ключа. Поэтому, когда вы удаляете ключ из словаря, эта ссылка удаляется, а не объект, на который ссылаются. Сам объект может быть впоследствии удален сборщиком мусора автоматически, если в памяти нет других ссылок на него. Создание глубокой копии требует больше вычислений по сравнению с мелкой копией, поэтому она снижает производительность кода, делая копию, тратя память и предоставляя больше работы ГХ, иногда достаточно мелкой копии.

Однако, если у вас есть изменяемые объекты в качестве значений словаря и вы планируете изменить их позже в возвращаемом словаре без ключа, вы должны сделать глубокую копию.

С мелкой копией:

def get_dict_wo_key(dictionary, key):
    """Returns a **shallow** copy of the dictionary without a key."""
    _dict = dictionary.copy()
    _dict.pop(key, None)
    return _dict


d = {"a": [1, 2, 3], "b": 2, "c": 3}
key_to_remove = "c"

new_d = get_dict_wo_key(d, key_to_remove)
print(d)  # {"a": [1, 2, 3], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3], "b": 2}
new_d["a"].append(100)
print(d)  # {"a": [1, 2, 3, 100], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3, 100], "b": 2}
new_d["b"] = 2222
print(d)  # {"a": [1, 2, 3, 100], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3, 100], "b": 2222}

С глубокой копией:

from copy import deepcopy


def get_dict_wo_key(dictionary, key):
    """Returns a **deep** copy of the dictionary without a key."""
    _dict = deepcopy(dictionary)
    _dict.pop(key, None)
    return _dict


d = {"a": [1, 2, 3], "b": 2, "c": 3}
key_to_remove = "c"

new_d = get_dict_wo_key(d, key_to_remove)
print(d)  # {"a": [1, 2, 3], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3], "b": 2}
new_d["a"].append(100)
print(d)  # {"a": [1, 2, 3], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3, 100], "b": 2}
new_d["b"] = 2222
print(d)  # {"a": [1, 2, 3], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3, 100], "b": 2222}
Никита
источник
21

… Как я могу удалить элемент из словаря, чтобы вернуть копию (то есть, не изменяя оригинал)?

A dictнеправильная структура данных, чтобы использовать для этого.

Конечно, копирование dict и извлечение из копий работает, как и создание нового dict с пониманием, но все это копирование требует времени - вы заменили операцию с постоянным временем на операцию с линейным временем. И все эти живые копии сразу занимают место - линейное пространство за копию.

Другие структуры данных, такие как попытки сопоставления хеш-массива , предназначены именно для этого варианта использования: добавление или удаление элемента возвращает копию в логарифмическом времени, разделяя большую часть его хранилища с оригиналом . 1

Конечно, есть некоторые недостатки. Производительность скорее логарифмическая, чем постоянная (хотя с большим основанием, обычно 32-128). И, хотя вы можете сделать API-интерфейс без мутаций идентичным, API-интерфейс с dictмутациями, очевидно, отличается. И, самое главное, в Python нет батарей HAMT. 2

pyrsistentБиблиотека представляет собой довольно твердый реализация HAMT на основе Dict-заменителей (и различных других типов) для Python. У него даже есть отличный API для эволюции, позволяющий как можно более плавно портировать существующий изменяющийся код в постоянный код. Но если вы хотите быть откровенным о возврате копий, а не о мутировании, просто используйте его так:

>>> from pyrsistent import m
>>> d1 = m(a=1, b=2)
>>> d2 = d1.set('c', 3)
>>> d3 = d1.remove('a')
>>> d1
pmap({'a': 1, 'b': 2})
>>> d2
pmap({'c': 3, 'a': 1, 'b': 2})
>>> d3
pmap({'b': 2})

Это d3 = d1.remove('a')именно то, о чем вопрос.

Если у вас есть изменяемые структуры данных, такие как dictи listвстроенные в pmap, у вас по-прежнему будут проблемы с алиасами - вы можете это исправить, только сделав неизменным весь путь, внедрив pmaps и pvectors.


1. HAMT также стали популярными в таких языках, как Scala, Clojure, Haskell, потому что они очень хорошо работают с программированием без блокировок и программной транзакционной памятью, но ни один из них не очень актуален в Python.

2. На самом деле, это HAMT в STDLIB, используемых в реализации contextvars. Ранее отозванный ПКП объясняет почему. Но это скрытая деталь реализации библиотеки, а не публичный тип коллекции.

abarnert
источник
19
d = {1: 2, '2': 3, 5: 7}
del d[5]
print 'd = ', d

Результат: d = {1: 2, '2': 3}

satels
источник
14

Просто позвоните del d ['key'].

Тем не менее, на производстве всегда полезно проверять, существует ли «ключ» в d.

if 'key' in d:
    del d['key']
Хан Хуа
источник
7
Хм, нет, в производстве лучше следовать идеологии EAFP . Просто удалите ключ в try-exceptблоке. По крайней мере, это будет атомная операция;)
Макскорюков
1
А если хочешь быть лаконичным - d.pop('key', None)пользуйся, это один лайнер. Но на самом деле вопрос заключался в том, чтобы получить словарь без единого ключа, а не в изменении слова. Так что понимание - это хороший выбор здесь;)
maxkoryukov
7

Нет, другого пути нет

def dictMinus(dct, val):
   copy = dct.copy()
   del copy[val]
   return copy

Тем не менее, часто создавать копии только слегка измененных словарей, вероятно, не очень хорошая идея, поскольку это приводит к сравнительно большим требованиям к памяти. Обычно лучше зарегистрировать старый словарь (если даже необходимо), а затем изменить его.

phihag
источник
7
# mutate/remove with a default
ret_val = body.pop('key', 5)
# no mutation with a default
ret_val = body.get('key', 5)
daino3
источник
5
>>> def delete_key(dict, key):
...     del dict[key]
...     return dict
... 
>>> test_dict = {'one': 1, 'two' : 2}
>>> print delete_key(test_dict, 'two')
{'one': 1}
>>>

это не делает никакой обработки ошибок, это предполагает, что ключ находится в dict, Вы могли бы хотеть проверить это сначала, и raiseесли это не

TMC
источник
10
Чем ваш метод отличается от простого del test_dict[key]?
Безумный физик
5

Здесь подход дизайна верхнего уровня:

def eraseElement(d,k):
    if isinstance(d, dict):
        if k in d:
            d.pop(k)
            print(d)
        else:
            print("Cannot find matching key")
    else:
        print("Not able to delete")


exp = {'A':34, 'B':55, 'C':87}
eraseElement(exp, 'C')

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

Вывод: {'B': 55, 'A': 34}

Надеюсь, это поможет!

атлас
источник
3

Ниже фрагмент кода поможет вам определенно, я добавил комментарии в каждой строке, которые помогут вам в понимании кода.

def execute():
   dic = {'a':1,'b':2}
   dic2 = remove_key_from_dict(dic, 'b')  
   print(dict2)           # {'a': 1}
   print(dict)            # {'a':1,'b':2}

def remove_key_from_dict(dictionary_to_use, key_to_delete):
   copy_of_dict = dict(dictionary_to_use)     # creating clone/copy of the dictionary
   if key_to_delete in copy_of_dict :         # checking given key is present in the dictionary
       del copy_of_dict [key_to_delete]       # deleting the key from the dictionary 
   return copy_of_dict                        # returning the final dictionary

или вы также можете использовать dict.pop ()

d = {"a": 1, "b": 2}

res = d.pop("c")  # No `KeyError` here
print (res)       # this line will not execute

или лучший подход

res = d.pop("c", "key not found")
print (res)   # key not found
print (d)     # {"a": 1, "b": 2}

res = d.pop("b", "key not found")
print (res)   # 2
print (d)     # {"a": 1}
Маюр Агарвал
источник
2

Вот еще один вариант, использующий понимание списка:

original_d = {'a': None, 'b': 'Some'}
d = dict((k,v) for k, v in original_d.iteritems() if v)
# result should be {'b': 'Some'}

Подход основан на ответе из этого поста: эффективный способ удаления ключей с пустыми строками.

BigBlueHat
источник
1
Если вы собираетесь ответить на давний вопрос, на который уже есть простой, соответствующий, принятый ответ, по крайней мере, убедитесь, что ваш ответ правильный. Это не делает то, что просил ОП.
user2357112 поддерживает Monica
Обычно я не проверяю даты по вопросам, которые, как мне кажется, могут содержать ценную информацию. Кроме того, в одном из комментариев к вопросу, на который я ссылался: «Обычно это именно то, что кто-то хочет, и, вероятно, это то, что нужно OP, но это не то, что запрашивал OP» stackoverflow.com/questions/12118695/… I знал, что это не был прямой ответ на вопрос; скорее расширение к опциям.
BigBlueHat
3
Этот ответ, хотя и не полный, позволяет нам узнать, что мы можем также удалять элементы, если есть условия. просто переходя if vк if k is not 'a'ответам op. Но я не думаю, что это эффективный способ, это удаляет элемент в O (n), а не O (log n), как pop или del.
Holgac
0
    species = {'HI': {'1': (1215.671, 0.41600000000000004),
  '10': (919.351, 0.0012),
  '1025': (1025.722, 0.0791),
  '11': (918.129, 0.0009199999999999999),
  '12': (917.181, 0.000723),
  '1215': (1215.671, 0.41600000000000004),
  '13': (916.429, 0.0005769999999999999),
  '14': (915.824, 0.000468),
  '15': (915.329, 0.00038500000000000003),
 'CII': {'1036': (1036.3367, 0.11900000000000001), '1334': (1334.532, 0.129)}}

Следующий код создаст копию dict speciesи удалит элементы, которые не находятся вtrans_HI

trans_HI=['1025','1215']
for transition in species['HI'].copy().keys():
    if transition not in trans_HI:
        species['HI'].pop(transition)
Sameeresque
источник