Dictionary 如何迭代两个字典并获取同一路径上的值?
我有两个字典,它们的结构相似……这意味着它们(应该)具有相同的键结构,即使在嵌套键中也是如此。此外,这些dict几乎可以有任何类型的嵌套结构…列表、dict等等。。。我希望能够遍历这些字典,获取这两个值并从函数返回它们 简单的例子:Dictionary 如何迭代两个字典并获取同一路径上的值?,dictionary,python,Dictionary,Python,我有两个字典,它们的结构相似……这意味着它们(应该)具有相同的键结构,即使在嵌套键中也是如此。此外,这些dict几乎可以有任何类型的嵌套结构…列表、dict等等。。。我希望能够遍历这些字典,获取这两个值并从函数返回它们 简单的例子: dict_a = {'a':1, 'b':2, 'c':{'d':3}} dict_b = {'a':2, 'b':4, 'c':{'d':6}} #Note the structure is the same for these dicts #I want to
dict_a = {'a':1, 'b':2, 'c':{'d':3}}
dict_b = {'a':2, 'b':4, 'c':{'d':6}}
#Note the structure is the same for these dicts
#I want to be able to do something like:
>>get_values( dict_a, dict_b)
[(1,2),(2,4),(3,6)]
我自己想出了一个解决方案,通过遍历一个字典,将每个键(或索引,如果遇到列表)附加到一个列表中…作为一种键路径:
key_map = []#A list of all key-paths for a dictionary
generate_key_paths(dict_a, [], key_map)
def generate_key_paths(value, key_list,key_map ):
new_list = [item for item in key_list]
if isinstance( value, dict):
#Handle list
for key, val in value.iteritems():
new_list.append( key)
self._generate_key_paths( val, new_list, key_map )
new_list = [item for item in key_list]
elif isinstance( value, list ):
#Handle list
for idx,item in enumerate(value):
new_list.append( idx )
self._generate_key_paths( item, new_list, key_map )
new_list = [item for item in key_list]
else:
#Handle data--reached farthest point you can go
#So just append (key-path, value) to key_map
key_map.append((new_list, value ) )
一旦你有了一个关键路径列表,值元组…就选择这个路径,并尝试到达它
在第二个字典上获取其值
val_list = []
for item in key_map:
value = get_value( item[0] )
if value is not None:
val_list.append( (item[1], value ) )
def get_value( key_list ):
value = dict_b
for item in key_list:
try:
value = value[item]
except:
value = None
break
return value
这对于字典可能具有的所有结构都非常有效,但似乎工作量很大。有没有一种更像蟒蛇的方式来实现这一点?有没有更快、更有效的方法
编辑:我正在寻找一个不是列表或dict的值,所以当达到这些值时,它应该在这些值中迭代,直到找到一个值为止。可以保证,如果它是一个列表,那么它将是一个dict列表,因此应该始终遵循某种键:值关系
例如,一个可能的dict可能如下所示:
dict_a={'a':1,'b':2,'c':[{'d':5},{'e':6}]}
dict_b={'a':2,'b':4,'c':[{'d':10},{'e':12}}
回答:
[(1,2)、(2,4)、(5,10)、(6,12)]
您正在寻找一个等价的扁平化(zipTree(…)
(函数不存在,但其名称应该能让我理解)
演示:
您还可以通过如下方式扩展函数来生成路径元组:
from collections import Mapping
def treezipItems(t1,t2, path=[]):
if isinstance(t1,Mapping) and isinstance(t2,Mapping):
assert set(t1)==set(t2)
for k,v1 in t1.items():
v2 = t2[k]
for tuple in treezipItems(v1,v2, path=path+[k]):
yield tuple
else:
yield (path, (t1,t2))
>>> list( treezipItems(dict_a, dict_b) )
[(['a'], (1, 2)), (['c', 'd'], (3, 6)), (['b'], (2, 4))]
我觉得这里最自然的是一个叫做treezip
的函数:
def treezip(t1,t2):
if isinstance(t1,Mapping) and isinstance(t2,Mapping):
assert set(t1)==set(t2)
R = {}
for k,v1 in t1.items():
v2 = t2[k]
R[k] = treezip(v1,v2)
return R
else:
return (t1,t2)
>>> from pprint import pprint as pp
>>> treezip(dict_a, dict_b)
{'a': (1, 2), 'c': {'d': (3, 6)}, 'b': (2, 4)}
然后是一个名为flattevalues
(或者flatteitems
的函数,如果您想保留关键帧)
这是第1个版本,它只是解包和合并
def gen(dict1, dict2):
for key in dict1:
if isinstance(dict1[key],dict):
for item in gen(dict1[key],dict2[key]):
yield item
else:
yield dict1[key], dict2[key]
# prints [(1, 2), (4, 7), (3, 6), (2, 4)]
print list(gen(dict_a, dict_b))
这里是第二个版本,它也对源代码进行排序(但我认为,对结果列表进行排序更好)
此src基于展平生成器,这可以在许多不同的TUT中找到非常好的答案。我只是想知道,对于
assert
,您是否可以执行assert set(t1)=set(t2)
。可能更快。回答得很好。@alan:噢,我真是太傻了,谢谢你。=)fixed@ninjagecko谢谢你的回答,但是对于可能有一个嵌套的dict列表作为一个值的情况,它似乎不起作用,其中每个dict也可能包含值。例如:dict_a={'a':2,'b':3,c:[{'d':4},{'e':6}]}。也许我的描述不够清楚,但我想向下迭代,直到它达到一个值…其中值不是dict或列表。elif-isinstance(t1,list)和isinstance(t2,list):对于idx,枚举中的项(t1):对于treeZip(v1,v2)中的元组,v1=item v2=t2[idx]:yield tuple
将此elif块添加到您的代码中似乎可以做到这一点!谢谢你的回答。
def treezip(t1,t2):
if isinstance(t1,Mapping) and isinstance(t2,Mapping):
assert set(t1)==set(t2)
R = {}
for k,v1 in t1.items():
v2 = t2[k]
R[k] = treezip(v1,v2)
return R
else:
return (t1,t2)
>>> from pprint import pprint as pp
>>> treezip(dict_a, dict_b)
{'a': (1, 2), 'c': {'d': (3, 6)}, 'b': (2, 4)}
dict_a = {'a':1, 'b':2, 'c':{'d':3,'e':{'f':4}}}
dict_b = {'a':2, 'b':4, 'c':{'d':6,'e':{'f':7}}}
def gen(dict1, dict2):
for key in dict1:
if isinstance(dict1[key],dict):
for item in gen(dict1[key],dict2[key]):
yield item
else:
yield dict1[key], dict2[key]
# prints [(1, 2), (4, 7), (3, 6), (2, 4)]
print list(gen(dict_a, dict_b))
def gen(dict1, dict2):
for key in sorted(dict1):
if isinstance(dict1[key],dict):
for item in gen(dict1[key],dict2[key]):
yield item
else:
yield dict1[key], dict2[key]
print list(gen(dict_a, dict_b))
# prints [(1, 2), (2, 4), (3, 6), (4, 7)]
print list(gen(dict_a,dict_b))