gpt4 book ai didi

python - 递归删除字典键?

转载 作者:太空狗 更新时间:2023-10-30 02:11:43 25 4
gpt4 key购买 nike

我将 Python 2.7 与 pliSTLib 结合使用,以嵌套字典/数组形式导入 .plist,然后查找特定键并在我看到它的任何地方将其删除。

当涉及到我们在办公室使用的实际文件时,我已经知道在哪里可以找到这些值——但我编写脚本的想法是我不知道,希望我不会'如果文件结构发生变化,或者我们需要对其他类似文件进行类似的操作,则不必在将来进行更改。

不幸的是,我似乎试图在遍历字典的同时修改它,但我不确定这实际上是如何发生的,因为我正在使用 iteritems()enumerate( ) 获取生成器并使用它们而不是我实际使用的对象。

def scrub(someobject, badvalue='_default'): ##_default isn't the real variable
"""Walks the structure of a plistlib-created dict and finds all the badvalues and viciously eliminates them.

Can optionally be passed a different key to search for."""
count = 0

try:
iterator = someobject.iteritems()
except AttributeError:
iterator = enumerate(someobject)

for key, value in iterator:
try:
scrub(value)
except:
pass
if key == badvalue:
del someobject[key]
count += 1

return "Removed {count} instances of {badvalue} from {file}.".format(count=count, badvalue=badvalue, file=file)

不幸的是,当我在测试 .plist 文件上运行它时,出现以下错误:

Traceback (most recent call last):
File "formscrub.py", line 45, in <module>
scrub(loadedplist)
File "formscrub.py", line 19, in scrub
for key, value in iterator:
RuntimeError: dictionary changed size during iteration

所以问题可能是对自身的递归调用,但即便如此,它不应该只是从原始对象中删除吗?我不确定如何避免递归(或者这是否是正确的策略),但由于它是一个 .plist,所以我确实需要能够确定什么时候是字典或列表,并迭代它们以寻找 (a) 更多要搜索的指令,或 (b) 导入的 .plist 中我需要删除的实际键值对。

最终,这不是部分问题,因为我将定期使用的文件具有已知结构。但是,我真的希望创建一些不关心它正在处理的对象的嵌套或顺序的东西,只要它是一个包含数组的 Python 字典即可。

最佳答案

在遍历序列时向序列添加或从序列中删除项目充其量是棘手的,并且对于字典来说是非法的(正如您刚刚发现的那样)。在迭代时从字典中删除条目的正确方法是迭代键的快照。在 Python 2.x 中,dict.keys() 提供了这样一个快照。所以对于听写,解决方案是:

for key in mydict.keys():
if key == bad_value:
del mydict[key]

正如 cpizza 在评论中提到的,对于 python3,您需要使用 list() 明确地创建快照:

for key in list(mydict.keys()):
if key == bad_value:
del mydict[key]

对于列表,尝试迭代索引的快照(即 for i in len(thelist):)将在删除任何内容后立即导致 IndexError(显然至少因为最后一个索引将不再存在),即使不存在,您也可能会跳过一个或多个项目(因为删除一个项目会使索引序列与列表本身不同步)。 enumerate 对 IndexError 是安全的(因为当列表中没有更多“下一个”项目时迭代将自行停止,但您仍然会跳过项目:

>>> mylist = list("aabbccddeeffgghhii")
>>> for x, v in enumerate(mylist):
... if v in "bdfh":
... del mylist[x]
>>> print mylist
['a', 'a', 'b', 'c', 'c', 'd', 'e', 'e', 'f', 'g', 'g', 'h', 'i', 'i']

如您所见,并不是很成功。

这里已知的解决方案是迭代反向索引,即:

>>> mylist = list("aabbccddeeffgghhii")
>>> for x in reversed(range(len(mylist))):
... if mylist[x] in "bdfh":
... del mylist[x]
>>> print mylist
['a', 'a', 'c', 'c', 'e', 'e', 'g', 'g', 'i', 'i']

这也适用于反向枚举,但我们并不关心。

总结一下:你需要两个不同的代码路径来处理字典和列表——你还需要处理“非容器”值(既不是列表也不是字典的值),这是你在你的代码中没有处理的当前代码。

def scrub(obj, bad_key="_this_is_bad"):
if isinstance(obj, dict):
# the call to `list` is useless for py2 but makes
# the code py2/py3 compatible
for key in list(obj.keys()):
if key == bad_key:
del obj[key]
else:
scrub(obj[key], bad_key)
elif isinstance(obj, list):
for i in reversed(range(len(obj))):
if obj[i] == bad_key:
del obj[i]
else:
scrub(obj[i], bad_key)

else:
# neither a dict nor a list, do nothing
pass

作为旁注:永远不要写一个简单的 except 子句。从来没有曾经。这应该是非法语法,真的。

关于python - 递归删除字典键?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20692710/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com