在对 Python 字典进行迭代时删除其中的项目可以吗?

2025-01-14 08:50:00
admin
原创
124
摘要:问题描述:假设我们有一个 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只是一些黑盒转换。)换句话说,我们...

问题描述:

假设我们有一个 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

这个定义清楚吗?你能提供一些参考资料来支持你的答案吗?


另请参阅如何避免“RuntimeError:迭代期间字典大小发生变化”错误?以了解如何避免该问题的单独问题。


解决方案 1:

亚历克斯·马特利 (Alex Martelli)在此对此进行了评论。

在循环遍历容器时更改容器(例如 dict)可能不安全。所以del d[f(k)]可能不安全。如您所知,解决方法是使用d.copy().items()(循环遍历容器的独立副本)而不是d.iteritems()d.items()(使用相同的底层容器)。

可以修改字典现有索引处的值,但在新索引处插入值(例如d[g(k)] = v)可能不起作用。

解决方案 2:

Python 文档页面(针对Python 2.7)上明确提到

iteritems()在字典中添加或删除条目时使用可能会引发RuntimeError或无法遍历所有条目。

对于Python 3来说也是如此。

iter(d)对于、d.iterkeys()和也是如此d.itervalues(),我甚至会说对于 也是如此for k, v in d.items():(我不记得具体是什么了for,但如果实现调用 ,我不会感到惊讶iter(d))。

解决方案 3:

你不能这样做,至少对于 来说不能d.iteritems()。我试过了,Python 失败了,

RuntimeError: dictionary changed size during iteration

如果您改用d.items(),那么它就会起作用。

在 Python 3 中,d.items()是字典的视图,就像d.iteritems()在 Python 2 中一样。要在 Python 3 中执行此操作,请改用d.copy().items()。这同样允许我们迭代字典的副本,以避免修改我们正在迭代的数据结构。

解决方案 4:

我有一个包含 Numpy 数组的大型字典,因此 @murgatroid99 建议的 dict.copy().keys() 方法是行不通的(尽管它有效)。相反,我只是将 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 内部运作的哲学领域,但它确实为所述问题提供了实用的解决方案。

解决方案 5:

下面的代码表明这个定义不太明确:

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

解决方案 6:

对于 Python 3,你应该:

prefix = 'item_'
t = {'f1': 'ffw', 'f2': 'fca'}
t2 = dict() 
for k,v in t.items():
    t2[k] = prefix + v

或使用:

t2 = t1.copy()

您永远不应该修改原始字典,否则会导致混乱以及潜在的错误或运行时错误。除非您只是将新的键名附加到字典中。

解决方案 7:

这个问题询问如何使用迭代器(有趣的是,Python 2.iteritems迭代器在 Python 3 中不再受支持)来删除或添加项目,并且它*的唯一正确答案一定是“否”,因为您可以在接受的答案中找到它。然而:大多数搜索者都试图找到解决方案,他们不会关心从技术上如何做到这一点,无论是迭代器还是递归,并且这个问题有一个解决方案:*

如果不使用附加(递归)函数,则无法循环更改字典。

因此,该问题应与具有可行解决方案的问题相链接:

  • 如何删除深度嵌套字典中所选键所在的键值对?(=“删除”)

  • 它还很有用,因为它展示了如何在运行时更改字典的项目:如何在深度嵌套的字典中所选键出现的位置用其值替换键:值对?(=“替换”)。

通过相同的递归方法,您也可以根据问题的要求添加项目。


由于我链接此问题的请求被拒绝,因此这里提供了可以从字典中删除项目的解决方案的副本。请参阅如何删除深度嵌套字典中所选键所在的键:值对?(=“删除”)以获取示例/致谢/注释。

import copy

def find_remove(this_dict, target_key, bln_overwrite_dict=False):
    if not bln_overwrite_dict:
        this_dict = copy.deepcopy(this_dict)

    for key in this_dict:
        # if the current value is a dict, dive into it
        if isinstance(this_dict[key], dict):
            if target_key in this_dict[key]:
                this_dict[key].pop(target_key)

            this_dict[key] = find_remove(this_dict[key], target_key)

    return this_dict

dict_nested_new = find_remove(nested_dict, "sub_key2a")

诀窍

诀窍是在递归到达子级之前提前找出 target_key 是否在下一个子级中(= this_dict[key] = 当前字典迭代的值)。只有这样,您才能在迭代字典时删除子级的键值对。一旦您到达要删除的键所在的级别,然后尝试从那里删除它,您将收到错误:

RuntimeError: dictionary changed size during iteration

递归解决方案仅在下一个值的子级别上做出任何更改,因此避免了错误。

解决方案 8:

我遇到了同样的问题,并使用以下步骤解决了该问题。

即使在迭代过程中进行修改,Python 列表也可以进行迭代。因此对于以下代码,它将无限地打印 1。

for i in list:
   list.append(1)
   print 1

因此,通过协同使用 list 和 dict 就可以解决这个问题。

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

解决方案 9:

今天我有一个类似的用例,但不是在循环开始时简单地将字典上的键具体化,而是希望对字典的更改影响字典的迭代,这是一个有序字典。

我最终构建了以下例程,它也可以在 jaraco.itertools 中找到:

def _mutable_iter(dict):
    """
    Iterate over items in the dict, yielding the first one, but allowing
    it to be mutated during the process.
    >>> d = dict(a=1)
    >>> it = _mutable_iter(d)
    >>> next(it)
    ('a', 1)
    >>> d
    {}
    >>> d.update(b=2)
    >>> list(it)
    [('b', 2)]
    """
    while dict:
        prev_key = next(iter(dict))
        yield prev_key, dict.pop(prev_key)

文档字符串说明了用法。可以使用此函数代替d.iteritems()上述函数来达到所需的效果。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用