Изменение Python dict во время итерации по нему
Допустим, у нас есть словарь Python d
, и мы повторяем это так:
for k,v in d.iteritems():
del d[f(k)] # remove some item
d[g(k)] = v # add a new item
(f
и g
это просто некоторые преобразования черного ящика.)
другими словами, мы пытаемся добавить / удалить элементы в d
при итерации по нему с помощью iteritems
.
это хорошо определена? Не могли бы вы предоставить некоторые ссылки для поддержки Вашего ответа?
(это довольно очевидно, как исправить это, если он сломан, так что это не угол я после.)
6 ответов:
это явно упоминается на странице документа Python (для Python 2.7), что
используя
iteritems()
при добавлении или удалении записей в словаре может возникнутьRuntimeError
или не удается выполнить итерацию по всем записям.аналогично Python 3.
то же самое относится и к
iter(d)
,d.iterkeys()
иd.itervalues()
, и я дойду до того, что скажу, что это делает дляfor k, v in d.items():
(Я не могу точно вспомнить, чтоfor
делает, но Я не удивлюсь, если реализация называетсяiter(d)
).
Алекс Мартелли взвешивает это здесь.
это может быть небезопасно, чтобы изменить контейнер (например, dict) во время цикла над контейнером. Так что
del d[f(k)]
может быть небезопасно. Как вы знаете, обходной путь заключается в использованииd.items()
(перебирать независимую копию контейнера) вместоd.iteritems()
(который использует тот же самый основной контейнер).это нормально, чтобы изменить значение в элементе существующей индекс dict, но вставка значений в новые индексы (например,
d[g(k)]=v
) могут не работать.
вы не можете этого сделать, по крайней мере с
d.iteritems()
. Я попробовал, и Python терпит неудачу сRuntimeError: dictionary changed size during iteration
если вы используете
d.items()
, то он работает.В Python 3,
d.items()
это вид в словарь, какd.iteritems()
в Python 2. Чтобы сделать это в Python 3, вместо этого используйтеd.copy().items()
. Это также позволит нам перебирать копию словаря, чтобы избежать изменения структуры данных, которую мы перебираем.
следующий код показывает, что это не совсем точно определено:
def f(x): return x def g(x): return x+1 def h(x): return x+10 try: d = {1:"a", 2:"b", 3:"c"} for k, v in d.iteritems(): del d[f(k)] d[g(k)] = v+"x" print d except Exception as e: print "Exception:", e try: d = {1:"a", 2:"b", 3:"c"} for k, v in d.iteritems(): del d[f(k)] d[h(k)] = v+"x" print d except Exception as e: print "Exception:", e
первый пример вызывает g (k) и выдает исключение (словарь изменил размер во время итерации).
второй пример вызывает h( k) и не вызывает исключения, но выводит:
{21: 'axx', 22: 'bxx', 23: 'cxx'}
, который, глядя на код, кажется неправильным - я бы ожидал что-то вроде:
{11: 'ax', 12: 'bx', 13: 'cx'}
У меня есть большой словарь, содержащий массивы numpy, поэтому дикт.копия.)(keys () вещь, предложенная @murgatroid99, была невыполнима (хотя и работала). Вместо этого я просто преобразовал keys_view в список, и он отлично работал (в Python 3.4):
for item in list(dict_d.keys()): temp = dict_d.pop(item) dict_d['some_key'] = 1 # Some value
Я понимаю, что это не погружается в философскую область внутренней работы Python, как ответы выше, но это дает практическое решение заявленной проблемы.
Я получил ту же проблему, и я использовал следующую процедуру, чтобы решить эту проблему.
список Python может быть повторен, даже если вы изменяете его во время итерации. поэтому для следующего кода он будет печатать 1 бесконечно.
for i in list: list.append(1) print 1
таким образом, используя список и дикт совместно можно решить эту проблему.
d_list=[] d_dict = {} for k in d_list: if d_dict[k] is not -1: d_dict[f(k)] = -1 # rather than deleting it mark it with -1 or other value to specify that it will be not considered further(deleted) d_dict[g(k)] = v # add a new item d_list.append(g(k))