Как удалить элементы из словаря во время итерации по нему?
Является ли законным удалять элементы из словаря в Python, итерации по нему?
Например:
for k, v in mydict.iteritems():
if k == val:
del mydict[k]
Идея состоит в том, чтобы удалить элементы, которые не соответствуют определенному условию из словаря, вместо того, чтобы создавать новый словарь, подмножество которого повторяется.
Это хорошее решение? Есть ли более элегантные/эффективные способы?
Ответы
Ответ 1
РЕДАКТИРОВАТЬ:
Этот ответ не будет работать для 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
Как указано в ответе delnan, удаление записей вызывает проблемы, когда итератор пытается перейти к следующей записи. Вместо этого используйте метод 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}
Ответ 2
Вы также можете сделать это в два этапа:
remove = [k for k in mydict if k == val]
for k in remove: del mydict[k]
Мой любимый подход - это просто создать новый dict:
# 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)
Ответ 3
Вы не можете изменять коллекцию при ее итерации. Таким образом, безумие - в первую очередь, если вам разрешено удалять и удалять текущий элемент, итератору придется двигаться дальше (+1), а следующий вызов next
приведет вас к тому, что (+2), так что вы закончите тем, что пропустите один элемент (тот, который прямо за тем, который вы удалили). У вас есть два варианта:
- Скопируйте все ключи (или значения, или и то, и другое) в зависимости от того, что вам нужно), затем перебирайте их. Вы можете использовать
.keys()
et al для этого (в Python 3, передать результирующий итератор в list
). Однако может быть очень расточительным по пространству.
- Переходите к
mydict
, как обычно, сохраняя ключи для удаления в отдельной коллекции to_delete
. Когда вы закончите повторение mydict
, удалите все элементы в to_delete
из mydict
. Сохраняет некоторые (в зависимости от того, сколько ключей удалено и сколько останется) пробегает первый подход, но также требует еще несколько строк.
Ответ 4
Итерации вместо копии, например, возвращаемой items()
:
for k, v in list(mydict.items()):
Ответ 5
С 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 )
# {}
Я использую его, когда из словаря, использующего большую часть памяти, я хочу построить другой словарь (содержащий модификацию первого) без выполнения "копирования" и перегрузки ОЗУ.
Ответ 6
Вы можете использовать понимание словаря.
d = {k:d[k] for k in d if d[k] != val}
Ответ 7
list(mydict)
чистый для использования 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.
Ответ 8
Сначала вы можете создать список ключей для удаления, а затем перебрать этот список, удалив их.
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]
Ответ 9
Я попробовал описанные выше решения в Python3, но это, кажется, единственное, что работает для меня при хранении объектов в dict. По сути, вы делаете копию вашего dict() и перебираете его, удаляя записи в исходном словаре.
tmpDict = realDict.copy()
for key, value in tmpDict.items():
if value:
del(realDict[key])
Ответ 10
Существует способ, который может подойти, если элементы, которые вы хотите удалить, всегда находятся в "начале" итерации 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.