[英]python - recursively deleting dict keys?
我正在使用帶有plistlib
Python 2.7 以嵌套的 dict/array 形式導入 .plist,然后查找特定鍵並在我看到的任何地方將其刪除。
當涉及到我們在辦公室使用的實際文件時,我已經知道在哪里可以找到這些值——但我寫腳本的想法是我沒有,希望我不必如果文件結構發生變化,或者我們需要對其他類似文件做同樣的事情,請在將來進行更改。
不幸的是,我似乎在迭代時試圖修改 dict,但我不確定這實際上是如何發生的,因為我使用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,我確實需要能夠確定事物何時是 dicts 或列表,並遍歷它們以搜索(a)更多dicts 來搜索,或 (b) 導入的 .plist 中我需要刪除的實際鍵值對。
最終,這是一個部分非問題,因為我將定期使用的文件具有已知結構。 然而,我真的希望創建一些不關心它正在使用的對象的嵌套或順序的東西,只要它是一個包含數組的 Python dict。
在迭代此序列時向/從序列中添加或刪除項目充其量是棘手的,並且使用 dicts 只是非法的(正如您剛剛發現的那樣)。 在迭代時從 dict 中刪除條目的正確方法是迭代鍵的快照。 在 Python 2.x 中, dict.keys()
提供了這樣的快照。 所以對於 dicts 的解決方案是:
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子句。 從來沒有。 這應該是非法語法,真的。
這是@bruno desthuilliers 之一的通用版本,帶有callable
的鍵來測試。
def clean_dict(obj, func):
"""
This method scrolls the entire 'obj' to delete every key for which the 'callable' returns
True
:param obj: a dictionary or a list of dictionaries to clean
:param func: a callable that takes a key in argument and return True for each key to delete
"""
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 func(key):
del obj[key]
else:
clean_dict(obj[key], func)
elif isinstance(obj, list):
for i in reversed(range(len(obj))):
if func(obj[i]):
del obj[i]
else:
clean_dict(obj[i], func)
else:
# neither a dict nor a list, do nothing
pass
以及一個帶有正則表達式可調用的示例:
func = lambda key: re.match(r"^<div>", key)
clean_dict(obj, func)
def walk(d, badvalue, answer=None, sofar=None):
if sofar is None:
sofar = []
if answer is None:
answer = []
for k,v in d.iteritems():
if k == badvalue:
answer.append(sofar + [k])
if isinstance(v, dict):
walk(v, badvalue, answer, sofar+[k])
return answer
def delKeys(d, badvalue):
for path in walk(d, badvalue):
dd = d
while len(path) > 1:
dd = dd[path[0]]
path.pop(0)
dd.pop(path[0])
輸出
In [30]: d = {1:{2:3}, 2:{3:4}, 5:{6:{2:3}, 7:{1:2, 2:3}}, 3:4}
In [31]: delKeys(d, 2)
In [32]: d
Out[32]: {1: {}, 3: 4, 5: {6: {}, 7: {1: 2}}}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.