我不得不从字典中删除一些字段,这些字段的键位于列表中。所以我写了这个函数:
def delete_keys_from_dict(dict_del, lst_keys):
"""
Delete the keys present in lst_keys from the dictionary.
Loops recursively over nested dictionaries.
"""
dict_foo = dict_del.copy() #Used as iterator to avoid the 'DictionaryHasChanged' error
for field in dict_foo.keys():
if field in lst_keys:
del dict_del[field]
if type(dict_foo[field]) == dict:
delete_keys_from_dict(dict_del[field], lst_keys)
return dict_del
此代码有效,但不是很优雅,我确信有更好的解决方案。
答案 0 :(得分:18)
def delete_keys_from_dict(dict_del, lst_keys):
for k in lst_keys:
try:
del dict_del[k]
except KeyError:
pass
for v in dict_del.values():
if isinstance(v, dict):
delete_keys_from_dict(v, lst_keys)
return dict_del
答案 1 :(得分:11)
首先,我认为您的代码正在运行,而不是不优雅。没有直接的理由不使用您提供的代码。
但有些事情可能会更好:
您的代码包含以下行:
if type(dict_foo[field]) == dict:
这可以肯定得到改善。通常(另请参阅PEP8)您应该使用isinstance
而不是比较类型:
if isinstance(dict_foo[field], dict)
但是如果True
是dict_foo[field]
的子类,那么它也会返回dict
。如果您不想要,也可以使用is
代替==
。这将是勉强(并且可能不明显)更快。
如果您还想允许任意类似dict的对象,您可以更进一步测试它是否为collections.abc.MutableMapping
。对于True
和dict
子类以及明确实现该接口而没有子类化dict
的所有可变映射,这将是dict
,例如UserDict
:
>>> from collections import MutableMapping
>>> # from UserDict import UserDict # Python 2.x
>>> from collections import UserDict # Python 3.x - 3.6
>>> # from collections.abc import MutableMapping # Python 3.7+
>>> isinstance(UserDict(), MutableMapping)
True
>>> isinstance(UserDict(), dict)
False
通常,函数可以在场内修改数据结构或返回一个新的(修改过的)数据结构。仅举几个例子:list.append
,dict.clear
,dict.update
都会修改到位的数据结构和return None
。这样可以更容易地跟踪函数的功能。然而,这不是一个硬性规则,并且此规则始终存在有效的例外情况。不过我个人认为这样的函数不需要是一个例外,我只需删除return dict_del
行并让它隐式返回None
,但是YMMV。
您复制了字典,以避免在迭代过程中删除键值对时出现问题。但是,正如另一个答案已经提到的那样,您可以迭代应该删除的键并尝试删除它们:
for key in keys_to_remove:
try:
del dict[key]
except KeyError:
pass
这还有一个额外的好处,就是你不需要嵌套两个循环(可能更慢,特别是如果需要删除的密钥数量非常长)。 / p>
如果你不喜欢空的except
条款,你也可以使用:contextlib.suppress
(需要Python 3.4 +):
from contextlib import suppress
for key in keys_to_remove:
with suppress(KeyError):
del dict[key]
我会重命名一些变量,因为它们只是描述性的,甚至是误导性的:
delete_keys_from_dict
应该提一下子句处理,也许delete_keys_from_dict_recursive
。
dict_del
听起来像是一个删除的字典。我倾向于喜欢dictionary
或dct
之类的名称,因为函数名称已经描述了对字典所做的操作。
lst_keys
,同样在那里。我可能只在那里使用keys
。如果你想更具体一些像keys_sequence
这样的东西会更有意义,因为它接受任何sequence
(你只需要能够多次迭代多次),而不是只是列出。
dict_foo
,只是没有......
field
也不合适,它是键。
正如我之前所说的,我个人会在原地修改字典而不再次返回字典。因此,我提出了两个解决方案,一个在原地修改它但没有返回任何内容的解决方案,另一个解决了删除键的新字典。
就地修改的版本(非常像Ned Batchelders解决方案):
from collections import MutableMapping
from contextlib import suppress
def delete_keys_from_dict(dictionary, keys):
for key in keys:
with suppress(KeyError):
del dictionary[key]
for value in dictionary.values():
if isinstance(value, MutableMapping):
delete_keys_from_dict(value, keys)
返回新对象的解决方案:
from collections import MutableMapping
def delete_keys_from_dict(dictionary, keys):
keys_set = set(keys) # Just an optimization for the "if key in keys" lookup.
modified_dict = {}
for key, value in dictionary.items():
if key not in keys_set:
if isinstance(value, MutableMapping):
modified_dict[key] = delete_keys_from_dict(value, keys_set)
else:
modified_dict[key] = value # or copy.deepcopy(value) if a copy is desired for non-dicts.
return modified_dict
然而,它只会复制字典,其他值不会作为副本返回,如果你愿意的话,你可以轻松地将它们包装在copy.deepcopy
中(我在代码的适当位置放置注释)。
答案 2 :(得分:9)
由于问题要求优雅,我会将我的通用解决方案提交给争论嵌套结构。首先,使用pip install boltons
安装boltons utility package,然后:
from boltons.iterutils import remap
data = {'one': 'remains', 'this': 'goes', 'of': 'course'}
bad_keys = set(['this', 'is', 'a', 'list', 'of', 'keys'])
drop_keys = lambda path, key, value: key not in bad_keys
clean = remap(data, visit=drop_keys)
print(clean)
# Output:
{'one': 'remains'}
简而言之,the remap utility是处理通常嵌套的真实数据结构的全功能但简洁的方法,甚至可以包含循环和特殊容器。
This page还有更多示例,包括使用Github API中更大对象的示例。
它是纯Python,因此它可以在任何地方使用,并且在Python 2.7和3.3+中进行了全面测试。最重要的是,我为这样的情况编写了它,所以如果你找到一个它无法处理的情况,你可以告诉我修复它right here。
答案 3 :(得分:2)
由于你已经需要遍历dict中的每个元素,我会坚持使用一个循环,并确保使用一个集合来查找要删除的键
def delete_keys_from_dict(dict_del, the_keys):
"""
Delete the keys present in the lst_keys from the dictionary.
Loops recursively over nested dictionaries.
"""
# make sure the_keys is a set to get O(1) lookups
if type(the_keys) is not set:
the_keys = set(the_keys)
for k,v in dict_del.items():
if k in the_keys:
del dict_del[k]
if isinstance(v, dict):
delete_keys_from_dict(v, the_keys)
return dict_del
答案 4 :(得分:2)
def delete_keys_from_dict(d, to_delete):
if isinstance(to_delete, str):
to_delete = [to_delete]
if isinstance(d, dict):
for single_to_delete in set(to_delete):
if single_to_delete in d:
del d[single_to_delete]
for k, v in d.items():
delete_keys_from_dict(v, to_delete)
elif isinstance(d, list):
for i in d:
delete_keys_from_dict(i, to_delete)
return d
d = {'a': 10, 'b': [{'c': 10, 'd': 10, 'a': 10}, {'a': 10}], 'c': 1 }
delete_keys_from_dict(d, ['a', 'c'])
>>> {'b': [{'d': 10}, {}]}
此解决方案适用于给定嵌套dict
中的list
和dict
。输入to_delete
可以是要删除的list
中的str
,也可以是单个str
。
请注意,如果您删除dict
中的唯一键,则会得到一个空的dict
。
答案 5 :(得分:1)
我认为以下更优雅:
def delete_keys_from_dict(dict_del, lst_keys):
if not isinstance(dict_del, dict):
return dict_del
return {key:value for key,value in ((key, delete_keys_from_dict(value)) for key,value in dict_del.items()) if key not in lst_keys}
答案 6 :(得分:0)
使用来自this帖子的精彩代码并添加一个小语句:
def remove_fields(self, d, list_of_keys_to_remove):
if not isinstance(d, (dict, list)):
return d
if isinstance(d, list):
return [v for v in (self.remove_fields(v, list_of_keys_to_remove) for v in d) if v]
return {k: v for k, v in ((k, self.remove_fields(v, list_of_keys_to_remove)) for k, v in d.items()) if k not in list_of_keys_to_remove}
答案 7 :(得分:0)
这适用于包含dict
的{{1}}个{Iterable
,...)。 Python3。对于Python 2 list
也应从迭代中排除。也可能有一些我不知道的无法使用的可迭代项。 (即将导致无限递归)
dict
答案 8 :(得分:0)
我来这里是为了寻找从深层嵌套的Python3字典中删除键的解决方案,所有解决方案似乎都有些复杂。
这是从嵌套或平面字典中删除键的单行代码:
nested_dict = {
"foo": {
"bar": {
"foobar": {},
"shmoobar": {}
}
}
}
>>> {'foo': {'bar': {'foobar': {}, 'shmoobar': {}}}}
nested_dict.get("foo", {}).get("bar", {}).pop("shmoobar", None)
>>> {'foo': {'bar': {'foobar': {}}}}
我使用.get()来获取KeyError,并且我还提供了空dict作为默认值,直到链的末尾。我为最后一个元素做pop(),并提供None作为默认值,以避免KeyError。