Удалить элемент из словаря - PullRequest
1148 голосов
/ 01 мая 2011

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

Кроме того, как я могу удалить элемент из словаря для возврата копии (т. Е. Без изменения оригинала)?

Ответы [ 14 ]

1491 голосов
/ 01 мая 2011

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

del d[key]

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

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

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


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

198 голосов
/ 21 марта 2014

pop изменяет словарь.

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

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

67 голосов
/ 01 мая 2011

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

>>> 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'}
52 голосов
/ 01 мая 2011

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
42 голосов
/ 21 сентября 2017

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

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

И оба они поднимут 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}
18 голосов
/ 01 мая 2011
d = {1: 2, '2': 3, 5: 7}
del d[5]
print 'd = ', d

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

13 голосов
/ 15 мая 2018

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

A dict - неправильная структура данных для использования.

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

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

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

Библиотека pyrsistent - довольно солидная реализация основанных на HAMT диктовок (и других типов) для Python. У него даже есть изящный evolver 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, у вас все еще будут проблемы с алиасами - вы можете это исправить, только сделав неизменным весь путь, внедрив pmap s и pvector s.


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

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

13 голосов
/ 02 марта 2015

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

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

if 'key' in d:
    del d['key']
.
7 голосов
/ 01 мая 2011

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

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

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

5 голосов
/ 25 января 2016

Здесь используется метод проектирования верхнего уровня:

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}

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

...