Как удалить элементы из словаря, перебирая его? - PullRequest
254 голосов
/ 22 марта 2011

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

Например:

for k, v in mydict.iteritems():
   if k == val:
     del mydict[k]

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

Это хорошее решение? Есть ли более элегантные / эффективные способы?

Ответы [ 10 ]

278 голосов
/ 22 марта 2011

РЕДАКТИРОВАТЬ:

Этот ответ не будет работать для Python3 и даст RuntimeError.

RuntimeError: словарь изменил размер во время итерации.

Это происходит потому, что mydict.keys() возвращает итератор, а не список.Как указано в комментариях, просто преобразуйте mydict.keys() в список на list(mydict.keys()), и он должен работать.


Простой тест в консоли показывает, что вы не можете изменять словарь при его повторении:

>>> mydict = {'one': 1, 'two': 2, 'three': 3, 'four': 4}
>>> for k, v in mydict.iteritems():
...    if k == 'two':
...        del mydict[k]
...
------------------------------------------------------------
Traceback (most recent call last):
  File "<ipython console>", line 1, in <module>
RuntimeError: dictionary changed size during iteration

Как указано в ответе Делнана, удаление записей вызывает проблемы, когда итератор пытается перейти к следующей записи.Вместо этого используйте метод keys(), чтобы получить список ключей и работать с ним:

>>> for k in mydict.keys():
...    if k == 'two':
...        del mydict[k]
...
>>> mydict
{'four': 4, 'three': 3, 'one': 1}

Если вам нужно удалить на основе значения элементов, используйте метод items() вместо:

>>> for k, v in mydict.items():
...     if v == 3:
...         del mydict[k]
...
>>> mydict
{'four': 4, 'one': 1}
80 голосов
/ 22 марта 2011

Вы также можете сделать это в два этапа:

remove = [k for k in mydict if k == val]
for k in remove: del mydict[k]

Мой любимый подход - это просто сделать новый диктант:

# Python 2.7 and 3.x
mydict = { k:v for k,v in mydict.items() if k!=val }
# before Python 2.7
mydict = dict((k,v) for k,v in mydict.iteritems() if k!=val)
18 голосов
/ 22 марта 2011

Вы не можете изменять коллекцию во время итерации.В этом и заключается безумие - особенно, если вам разрешат удалить и удалить текущий элемент, итератор должен будет двигаться дальше (+1), а следующий вызов next выведет вас за пределы этого (+2), поэтомув конечном итоге вы пропустите один элемент (тот, который находится сразу за тем, который вы удалили).У вас есть два варианта:

  • Скопируйте все ключи (или значения, или оба, в зависимости от того, что вам нужно), затем итерируйте их.Вы можете использовать .keys() и др. Для этого (в Python 3 передать полученный итератор на list).Хотя это может быть очень расточительно.
  • Итерируйте по mydict как обычно, сохраняя ключи для удаления в отдельной коллекции to_delete.Когда вы закончите итерацию mydict, удалите все элементы в to_delete из mydict.Сохраняет некоторое (в зависимости от того, сколько ключей удалено и сколько осталось) места в первом подходе, но также требуется еще несколько строк.
16 голосов
/ 22 марта 2011

Вместо этого переберите копию, например, возвращенную items():

for k, v in list(mydict.items()):
8 голосов
/ 10 октября 2016

При использовании python3 итерация в dic.keys () вызовет ошибку размера словаря. Вы можете использовать этот альтернативный способ:

Протестировано с python3, работает нормально, и ошибка " словарь изменил размер во время итерации " не возникает:

my_dic = { 1:10, 2:20, 3:30 }
# Is important here to cast because ".keys()" method returns a dict_keys object.
key_list = list( my_dic.keys() )

# Iterate on the list:
for k in key_list:
    print(key_list)
    print(my_dic)
    del( my_dic[k] )


print( my_dic )
# {}

Я использую его, когда из словаря, использующего много памяти, я хочу создать другой словарь (содержащий модификацию первого) без выполнения «копирования» и перегрузки ОЗУ.

7 голосов
/ 20 января 2018

Это самый чистый для использования list(mydict):

>>> mydict = {'one': 1, 'two': 2, 'three': 3, 'four': 4}
>>> for k in list(mydict):
...     if k == 'three':
...         del mydict[k]
... 
>>> mydict
{'four': 4, 'two': 2, 'one': 1}

Это соответствует параллельной структуре для списков:

>>> mylist = ['one', 'two', 'three', 'four']
>>> for k in list(mylist):                            # or mylist[:]
...     if k == 'three':
...         mylist.remove(k)
... 
>>> mylist
['one', 'two', 'four']

Оба работают в python2 и python3.

5 голосов
/ 19 мая 2017

Вы можете использовать словарь понимания.

d = {k:d[k] for k in d if d[k] != val}

4 голосов
/ 02 марта 2017

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

dict = {'one' : 1, 'two' : 2, 'three' : 3, 'four' : 4}
delete = []
for k,v in dict.items():
    if v%2 == 1:
        delete.append(k)
for i in delete:
    del dict[i]
3 голосов
/ 24 мая 2019

Существует способ, который может подойти, если элементы, которые вы хотите удалить, всегда находятся в «начале» итерации dict

while mydict:
    key, value = next(iter(mydict.items()))
    if should_delete(key, value):
       del mydict[key]
    else:
       break

«Начало» гарантированно будет согласованным только для определенных версий / реализаций Python. Например, из Что нового в Python 3.7

природа сохранения порядка вставки объектов dict объявлена ​​официальной частью спецификации языка Python.

Таким образом, избегается копия указания, которое предлагают многие другие ответы, по крайней мере, в Python 3.

1 голос
/ 05 апреля 2018

Я попробовал описанные выше решения в Python3, но, похоже, это единственное, что работает для меня при хранении объектов в dict. По сути, вы делаете копию вашего dict () и перебираете его, удаляя записи в исходном словаре.

        tmpDict = realDict.copy()
        for key, value in tmpDict.items():
            if value:
                del(realDict[key])
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...