Удаление элементов из словаря при повторении [duplicate]

204
задан Trilarion 22 April 2015 в 19:47
поделиться

9 ответов

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

>>> 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}

EDIT:

Выше не будет работать для Python3 и даст RuntimeError.

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

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

239
ответ дан Sumit Jha 19 August 2018 в 03:53
поделиться
  • 1
    Обратите внимание, что в Python 3, dict.items () возвращает итератор (и dict.iteritems () ушел). – Tim Lesher 27 September 2011 в 20:01
  • 2
    Чтобы прокомментировать комментарий @TimLesher ... Это не будет работать на Python 3. – max 26 January 2012 в 18:55
  • 3
    Чтобы подробно рассказать о разработке @ max, он будет работать, если вы конвертируете вышеуказанный код с 2to3. Один из исправлений по умолчанию сделает цикл похожим на for k, v in list(mydict.items()):, который отлично работает на Python 3. То же самое для keys() становится list(keys()). – Walter Mundt 15 August 2012 в 18:59
  • 4
    Это не работает. Я получаю сообщение об ошибке: RuntimeError: dictionary changed size during iteration – Tomáš Zato 18 December 2016 в 03:56
  • 5
    @ TomášZato, как указал Уолтер, для python3 вам нужно использовать for k in list(mydict.keys()):, поскольку python3 делает метод keys () итератором, а также запрещает удаление элементов dict во время итерации. Добавив вызов list (), вы превращаете итератор ключей () в список. Поэтому, когда вы находитесь в теле цикла for, вы больше не итерации по самому словарю. – Geoff Crompton 23 March 2017 в 00:12

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

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

4
ответ дан Aaron 19 August 2018 в 03:53
поделиться

С 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
ответ дан glihm 19 August 2018 в 03:53
поделиться

Итерации вместо копии, например, возвращаемой функцией items():

for k, v in list(mydict.items()):
13
ответ дан Ioannis Filippidis 19 August 2018 в 03:53
поделиться
  • 1
    Это не имеет особого смысла - тогда вы не можете del v напрямую, поэтому вы сделали копию каждого v, которую вы никогда не собираетесь использовать, и вам все равно нужно получить доступ к элементам по ключу. dict.keys() - лучший выбор. – Josh Caswell 22 March 2011 в 03:21
  • 2
    @Josh: Все зависит от того, сколько вам потребуется использовать v в качестве критерия для удаления. – Ignacio Vazquez-Abrams 22 March 2011 в 08:00
  • 3
    В Python 3, dict.items() возвращает итератор, а не копию. См. Комментарий к ответу Блэра , который (к сожалению) также предполагает семантику Python 2. – Cecil Curry 22 February 2016 в 06:37

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

        tmpDict = realDict.copy()
        for key, value in tmpDict.items():
            if value:
                del(realDict[key])
0
ответ дан JLandbrug 19 August 2018 в 03:53
поделиться

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

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)
67
ответ дан Nils 19 August 2018 в 03:53
поделиться
  • 1
    Стоит отметить, что такие «диктовские понимания» работает только в python 3 ... – senderle 22 March 2011 в 01:06
  • 2
    @senderle: Начиная с версии 2.7. – Jochen Ritzel 22 March 2011 в 01:17
  • 3
    Подход к пониманию дикта делает копию словаря; к счастью, значения, по крайней мере, не получают глубокого копирования, просто связаны. Тем не менее, если у вас много ключей, это может быть плохо. По этой причине мне больше нравится цикл remove. – max 26 January 2012 в 19:28
  • 4
    Вы также можете комбинировать следующие шаги: for k in [k for k in mydict if k == val]: del mydict[k] – AXO 16 January 2017 в 12:14
  • 5
    первое решение является единственным эффективным на больших dicts в этом потоке до сих пор - поскольку оно не делает полную копию. – kxr 28 April 2017 в 21:05

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

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]
4
ответ дан Pob 19 August 2018 в 03:53
поделиться
  • 1
    Это скорее дубликат первого решения @ Ritzel (эффективный на больших диктофонах без полной копии). Хотя "длинное чтение" без понимания списка. Но возможно ли, возможно, быстрее? – kxr 28 April 2017 в 21:08

Чистое использование 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.

1
ответ дан rsanden 19 August 2018 в 03:53
поделиться

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

  • Скопируйте все ключи (или значения или и то, и другое в зависимости от того, что вам нужно), а затем переберите их. Вы можете использовать .keys() et al для этого (в Python 3 передать результирующий итератор на list). Однако может быть очень расточительным по пространству.
  • Как обычно, переходите к mydict, сохраняя ключи для удаления в отдельной коллекции to_delete. Когда вы закончите повтор mydict, удалите все элементы в to_delete из mydict. Сохраняет некоторые (в зависимости от того, сколько ключей удалено и сколько осталось) пробегает первый подход, но также требует еще несколько строк.
18
ответ дан user 19 August 2018 в 03:53
поделиться
  • 1
    You can't modify a collection while iterating it. это правильно для диктов и друзей, но вы можете изменять списки во время итерации: L = [1,2,None,4,5] <\n> for n,x in enumerate(L): <\n\t> if x is None: del L[n] – Nils 29 February 2016 в 17:42
  • 2
    @Nils Это не исключение, но оно все равно неверно. Обратите внимание: codepad.org/Yz7rjDVT - см., Например, stackoverflow.com/q/6260089/395760 для объяснения – user 29 February 2016 в 18:19
  • 3
    Получил меня здесь. Тем не менее can't является правильным только для dict и друзей, в то время как для списков должно быть shouldn't. – Nils 29 February 2016 в 20:20
Другие вопросы по тегам:

Похожие вопросы: