, который позволяет удалять во время итерации

ОБНОВЛЕНО на основе ответа Леннарта Регебро

Предположим, вы просматриваете словарь и иногда вам нужно удалить элемент. Следующее очень эффективно:

remove = []
for k, v in dict_.items():
  if condition(k, v):
    remove.append(k)
    continue
  # do other things you need to do in this loop
for k in remove:
  del dict_[k]

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

Популярный подход к пониманию dict:

dict_ = {k : v for k, v in dict_ if not condition(k, v)}
for k, v in dict_.items():
  # do other things you need to do in this loop

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

Намного лучший подход - скопировать только ключи, а не весь словарь:

for k in list(dict_.keys()):
  if condition(k, dict_[k]):
    del dict_[k]
    continue
  # do other things you need to do in this loop       

(Обратите внимание, что все примеры кода написаны на Python 3, поэтому keys () , items () возвращает вид, а не копию.)

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

Тем не менее, мне интересно, можно ли избежать даже этого с помощью специального словаря, который позволяет удалять при итерации:

for k, v in dict_.items():
  if condition(k, v):
    del dict_[k]
    continue
  # do other things you need to do in this loop

Возможно, итератор всегда может смотреть вперед, так что при вызове __ next __ , итератор знает, куда идти, даже не глядя на текущий элемент (ему нужно будет посмотреть только на элемент, когда он впервые доберется до него). А если следующего элемента нет, итератор может просто установить флаг, который вызовет исключение StopIteration при повторном вызове __ next __ .

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

Есть ли проблемы с этим подходом?

Одна проблема в том, что я не уверен, что это можно сделать без материальных накладных расходов по сравнению с существующим dict ; в противном случае было бы быстрее использовать подход list (dict _) !

ОБНОВЛЕНИЕ:

Все версии перепробовал. Я не сообщаю о сроках, поскольку они явно очень зависят от конкретной ситуации. Но можно с уверенностью сказать, что во многих случаях самым быстрым подходом будет list (dict _) .В конце концов, если задуматься, копирование - это самая быстрая операция, которая растет линейно с размером списка; почти любые другие накладные расходы, если они также пропорциональны размеру списка, вероятно, будут больше.

Мне очень нравятся все идеи, но, поскольку мне нужно выбрать только одну, я принимаю решение диспетчера контекста, поскольку оно позволяет использовать словарь как нормальный, так и «расширенный» с очень небольшими изменениями кода.

26
задан max 6 February 2012 в 09:22
поделиться