我有一个包含大量条目的 dict
。我只对其中的一小部分感兴趣。有没有一种简单的方法来修剪所有其他的?
构建一个新的字典:
dict_you_want = { your_key: old_dict[your_key] for your_key in your_keys }
使用字典理解。
如果您使用缺少它们的版本(即 Python 2.6 和更早版本),请将其设为 dict((your_key, old_dict[your_key]) for ...)
。是一样的,虽然更丑。
请注意,与 jnnnnn 的版本不同,这对于任何大小的 old_dict
都具有稳定的性能(仅取决于 your_keys 的数量)。无论是速度还是内存。由于这是一个生成器表达式,它一次处理一个项目,它不会查看 old_dict 的所有项目。
就地删除所有内容:
unwanted = set(keys) - set(your_dict)
for unwanted_key in unwanted: del your_dict[unwanted_key]
稍微优雅的 dict 理解:
foodict = {k: v for k, v in mydict.items() if k.startswith('foo')}
mydict.iteritems()
,可能会有相同的性能。 .items()
创建另一个列表。
这是 python 2.6 中的一个示例:
>>> a = {1:1, 2:2, 3:3}
>>> dict((key,value) for key, value in a.iteritems() if key == 1)
{1: 1}
过滤部分是 if
语句。
如果您只想选择非常多的键中的几个,则此方法比 delnan 的答案慢。
if key in ('x','y','z')
。
您可以使用我的 funcy 库中的 project 函数来做到这一点:
from funcy import project
small_dict = project(big_dict, keys)
另请查看 select_keys。
代码 1:
dict = { key: key * 10 for key in range(0, 100) }
d1 = {}
for key, value in dict.items():
if key % 2 == 0:
d1[key] = value
代码 2:
dict = { key: key * 10 for key in range(0, 100) }
d2 = {key: value for key, value in dict.items() if key % 2 == 0}
代码 3:
dict = { key: key * 10 for key in range(0, 100) }
d3 = { key: dict[key] for key in dict.keys() if key % 2 == 0}
所有的一段代码性能都是用timeit用number=1000来衡量的,每段代码收集1000次。
https://i.stack.imgur.com/xqsJC.png
对于python 3.6,三种方式的过滤dict键的性能几乎相同。对于 python 2.7 代码 3 稍快一些。
这一个班轮 lambda 应该可以工作:
dictfilt = lambda x, y: dict([ (i,x[i]) for i in x if i in set(y) ])
这是一个例子:
my_dict = {"a":1,"b":2,"c":3,"d":4}
wanted_keys = ("c","d")
# run it
In [10]: dictfilt(my_dict, wanted_keys)
Out[10]: {'c': 3, 'd': 4}
这是一个基本的列表理解,遍历您的 dict 键(x 中的 i),如果键位于所需的键列表 (y) 中,则输出元组 (key,value) 对的列表。 dict() 将整个内容包装为 dict 对象输出。
wanted_keys
使用 set
,否则看起来不错。
dictfilt({'x':['wefwef',52],'y':['iuefiuef','efefij'],'z':['oiejf','iejf']}, ('x','z'))
,它会按预期返回 {'x': ['wefwef', 52], 'z': ['oiejf', 'iejf']}
。
dict={'0':[1,3], '1':[0,2,4], '2':[1,4]}
尝试了这个,结果是 {}
,我认为它是一个空白字典。
foo = {'0':[1,3], '1':[0,2,4], '2':[1,4]}; dictfilt(foo,('0','2'))
,我会得到:{'0': [1, 3], '2': [1, 4]}
,这是预期的结果
给定您的原始字典 orig
和您感兴趣的条目集 keys
:
filtered = dict(zip(keys, [orig[k] for k in keys]))
这不如 delnan 的答案好,但应该适用于每个感兴趣的 Python 版本。但是,对于原始字典中存在的 keys
的每个元素,它都是脆弱的。
基于 delnan 接受的答案。
如果您想要的钥匙之一不在 old_dict 中怎么办? delnan 解决方案将抛出一个您可以捕获的 KeyError 异常。如果这不是你需要的,也许你想:
仅包括在 old_dict 和您的想要的密钥集中都存在的密钥。 old_dict = {'name':"Foobar", 'baz':42} Wanted_keys = ['name', 'age'] new_dict = {k: old_dict[k] for k in set(wanted_keys) & set(old_dict.keys ())} >>> new_dict {'name': 'Foobar'} 具有未在 old_dict 中设置的键的默认值。 default = None new_dict = {k: old_dict[k] if k in old_dict else default for k in Wanted_keys} >>> new_dict {'age': None, 'name': 'Foobar'}
{k: old_dict.get(k, default) for k in ...}
这个函数可以解决问题:
def include_keys(dictionary, keys):
"""Filters a dict by only including certain keys."""
key_set = set(keys) & set(dictionary.keys())
return {key: dictionary[key] for key in key_set}
就像 delnan 的版本一样,这个使用字典理解并且对于大型字典具有稳定的性能(仅取决于您允许的键数,而不是字典中的键总数)。
就像 MyGGan 的版本一样,这个版本允许您的键列表包含字典中可能不存在的键。
作为奖励,这是相反的,您可以通过排除原始中的某些键来创建字典:
def exclude_keys(dictionary, keys):
"""Filters a dict by excluding certain keys."""
key_set = set(dictionary.keys()) - set(keys)
return {key: dictionary[key] for key in key_set}
注意,和delnan的版本不同的是,操作没有到位,所以性能和字典中key的个数有关。但是,这样做的好处是该函数不会修改提供的字典。
编辑:添加了一个单独的功能,用于从字典中排除某些键。
keys
通过任何类型的迭代,例如 set 接受的。
invert
是否意味着保留 keys
参数,或者 keys
参数被拒绝?”,他们中有多少人会同意?
另外的选择:
content = dict(k1='foo', k2='nope', k3='bar')
selection = ['k1', 'k3']
filtered = filter(lambda i: i[0] in selection, content.items())
但是您会得到由 filter()
返回的 list
(Python 2) 或迭代器 (Python 3),而不是 dict
。
filtered
包裹在 dict
中,即可取回字典!
如果我们想创建一个删除选定键的新字典,我们可以使用字典理解例如:
d = {
'a' : 1,
'b' : 2,
'c' : 3
}
x = {key:d[key] for key in d.keys() - {'c', 'e'}} # Python 3
y = {key:d[key] for key in set(d.keys()) - {'c', 'e'}} # Python 2.*
# x is {'a': 1, 'b': 2}
# y is {'a': 1, 'b': 2}
这在我看来是最简单的方法:
d1 = {'a':1, 'b':2, 'c':3}
d2 = {k:v for k,v in d1.items() if k in ['a','c']}
我也喜欢这样做来解压这些值:
a, c = {k:v for k,v in d1.items() if k in ['a','c']}.values()
if k in {'a','c'}
而不是 if k in ['a','c']
。
我们也可以通过更优雅的dict理解来实现这一点:
my_dict = {"a":1,"b":2,"c":3,"d":4}
filtdict = {k: v for k, v in my_dict.items() if k.startswith('a')}
print(filtdict)
根据问题的标题,人们会期望在适当的位置过滤字典-一些答案建议了这样做的方法-仍然不清楚一种明显的方法是什么-我添加了一些时间:
import random
import timeit
import collections
repeat = 3
numbers = 10000
setup = ''
def timer(statement, msg='', _setup=None):
print(msg, min(
timeit.Timer(statement, setup=_setup or setup).repeat(
repeat, numbers)))
timer('pass', 'Empty statement')
dsize = 1000
d = dict.fromkeys(range(dsize))
keep_keys = set(random.sample(range(dsize), 500))
drop_keys = set(random.sample(range(dsize), 500))
def _time_filter_dict():
"""filter a dict"""
global setup
setup = r"""from __main__ import dsize, collections, drop_keys, \
keep_keys, random"""
timer('d = dict.fromkeys(range(dsize));'
'collections.deque((d.pop(k) for k in drop_keys), maxlen=0)',
"pop inplace - exhaust iterator")
timer('d = dict.fromkeys(range(dsize));'
'drop_keys = [k for k in d if k not in keep_keys];'
'collections.deque('
'(d.pop(k) for k in list(d) if k not in keep_keys), maxlen=0)',
"pop inplace - exhaust iterator (drop_keys)")
timer('d = dict.fromkeys(range(dsize));'
'list(d.pop(k) for k in drop_keys)',
"pop inplace - create list")
timer('d = dict.fromkeys(range(dsize));'
'drop_keys = [k for k in d if k not in keep_keys];'
'list(d.pop(k) for k in drop_keys)',
"pop inplace - create list (drop_keys)")
timer('d = dict.fromkeys(range(dsize))\n'
'for k in drop_keys: del d[k]', "del inplace")
timer('d = dict.fromkeys(range(dsize));'
'drop_keys = [k for k in d if k not in keep_keys]\n'
'for k in drop_keys: del d[k]', "del inplace (drop_keys)")
timer("""d = dict.fromkeys(range(dsize))
{k:v for k,v in d.items() if k in keep_keys}""", "copy dict comprehension")
timer("""keep_keys=random.sample(range(dsize), 5)
d = dict.fromkeys(range(dsize))
{k:v for k,v in d.items() if k in keep_keys}""",
"copy dict comprehension - small keep_keys")
if __name__ == '__main__':
_time_filter_dict()
结果:
Empty statement 8.375600000000427e-05
pop inplace - exhaust iterator 1.046749841
pop inplace - exhaust iterator (drop_keys) 1.830537424
pop inplace - create list 1.1531293939999987
pop inplace - create list (drop_keys) 1.4512304149999995
del inplace 0.8008298079999996
del inplace (drop_keys) 1.1573763689999979
copy dict comprehension 1.1982901489999982
copy dict comprehension - small keep_keys 1.4407784069999998
因此,如果我们想就地更新,del 似乎是赢家 - dict 理解解决方案当然取决于正在创建的 dict 的大小,删除一半的键已经太慢了 - 所以如果你可以过滤,请避免创建新的 dict地方。
编辑以解决@mpen 的评论-我从keep_keys 计算了放置键(假设我们没有放置键)-我假设keep_keys/drop_keys 是此迭代的集合,或者需要很长时间。有了这些假设,del 仍然更快 - 但可以肯定的是:如果您有 drop 键的 (set, list, tuple),请选择 del
drop_keys
不是一个公平的比较。问题更类似于 keep_keys
。我们知道我们想要哪些键,而不是我们不想要哪些键。
drop_keys
这会减慢很多 pop/del 方法。将为此发布一些时间
drop_keys
(我假设保留键是 O(1) k in keep_keys
的集合),似乎 del 也胜过 dict 理解。可能这意味着创建一个包含 500 个条目的字典比创建一个包含 500 个元素的列表要慢一些:P
简写:
[s.pop(k) for k in list(s.keys()) if k not in keep]
正如大多数答案所暗示的那样,为了保持简洁性,我们必须创建一个重复的对象,无论是 list
还是 dict
。这个创建了一次性 list
,但删除了原始 dict
中的键。
您可以使用 python-benedict
,它是一个 dict 子类。
安装:pip install python-benedict
from benedict import benedict
dict_you_want = benedict(your_dict).subset(keys=['firstname', 'lastname', 'email'])
它在 GitHub 上是开源的:https://github.com/fabiocaccamo/python-benedict
免责声明:我是这个库的作者。
如果您事先知道否定集(又名 not
键):
v = {'a': 'foo', 'b': 'bar', 'command': 'fizz', 'host': 'buzz' }
args = {k: v[k] for k in v if k not in ["a", "b"]}
args # {'command': 'fizz', 'host': 'buzz'}
这是在一个衬里中使用 del
的另一种简单方法:
for key in e_keys: del your_dict[key]
e_keys
是要排除的键的列表。它会更新你的字典,而不是给你一个新的。
如果你想要一个新的输出字典,那么在删除之前制作一个字典的副本:
new_dict = your_dict.copy() #Making copy of dict
for key in e_keys: del new_dict[key]
for key in e_keys: del your_dict[key]
),您能否分析什么更快?
我们可以像这样简单地使用 lambda 函数:
>>> dict_filter = lambda x, y: dict([ (i,x[i]) for i in x if i in set(y) ])
>>> large_dict = {"a":1,"b":2,"c":3,"d":4}
>>> new_dict_keys = ("c","d")
>>> small_dict=dict_filter(large_dict, new_dict_keys)
>>> print(small_dict)
{'c': 3, 'd': 4}
>>>
这是我的方法,支持像 mongo 查询这样的嵌套字段。
如何使用:
>>> obj = { "a":1, "b":{"c":2,"d":3}}
>>> only(obj,["a","b.c"])
{'a': 1, 'b': {'c': 2}}
only
功能:
def only(object,keys):
obj = {}
for path in keys:
paths = path.split(".")
rec=''
origin = object
target = obj
for key in paths:
rec += key
if key in target:
target = target[key]
origin = origin[key]
rec += '.'
continue
if key in origin:
if rec == path:
target[key] = origin[key]
else:
target[key] = {}
target = target[key]
origin = origin[key]
rec += '.'
else:
target[key] = None
break
return obj
old_dict
中缺少的键表示其他地方存在错误,在这种情况下,我非常喜欢错误而不是默默地错误结果。