例如,我在这里:
doc = { 'A':1, 'B':1, 'C':{'C-A':2, 'C-B':{'C-B-A':3}}
然后我定义了一个这样的函数:
def get(doc, *args):
if(len(args) == 1):
return doc[args[0]]
if(len(args) == 2):
return doc[args[0]][args[1]]
if(len(args) == 3):
return doc[args[0]][args[1]][args[2]]
所以我可以得到这样的价值:
get(doc,'A') //return 1
get(doc,'C','C-A') //return 2
get(doc,'C','C-B') //return {'C-B-A':3}
get(doc,'C','C-B','C-B-A') //return 3
现在我的问题是,如果doc有任何深度,如何重写func get ?
答案 0 :(得分:2)
您可以使用reduce
:
In [17]: doc = { 'A':1, 'B':1, 'C':{'C-A':2, 'C-B':{'C-B-A':3}}}
...:
...: def get(doc, *args):
...: return reduce(dict.get, args, doc)
In [18]: get(doc, 'A')
Out[18]: 1
In [19]: get(doc, 'C', 'C-A')
Out[19]: 2
In [20]: get(doc, 'C', 'C-B', 'C-B-A')
Out[20]: 3
您可以将reduce
视为:
def reduce(function, arguments, initializer):
a = initializer
for b in arguments:
a = function(a, b)
return a
即。它“累积”值,在给定的参数上调用二进制函数。
在您的情况下,首先a
为doc
。然后,它成为doc[arguments[0]]
的结果。如果arguments
是长度为1,则迭代停止并返回结果,否则它是子查询之一,并且在下一次迭代中,循环将从该子字典中获取值。
请注意,使用递归意味着限制为sys.getrecursionlimit()
的深度,通常大约为1000,并且它将多更慢,因为您至少执行了两次函数调用检索值:
In [84]: def get(doc, *args):
...: return reduce(dict.get, args, doc)
In [85]: def get2(doc, *args):
...: return get2(doc[args[0]], *args[1:]) if args else doc
In [86]: %timeit get(doc, 'C', 'C-B', 'C-B-A')
1000000 loops, best of 3: 621 ns per loop
In [87]: %timeit get2(doc, 'C', 'C-B', 'C-B-A')
1000000 loops, best of 3: 1.04 us per loop
In [88]: d = make_dict(depth=350)
In [89]: %timeit get(d, *range(350))
10000 loops, best of 3: 38.9 us per loop
In [90]: %timeit get2(d, *range(350))
1000 loops, best of 3: 973 us per loop
这是一个浅薄的词典。如果你尝试使用更大深度的dict,与功能解决方案相比,递归解决方案将变得越来越慢。
答案 1 :(得分:2)
使用递归:
def get(doc, *args):
return get(doc[args[0]], *args[1:]) if args else doc
或者,如果出于某种原因,你更愿意迭代地进行
def get(doc, *args):
for arg in args:
doc = doc[arg]
return doc
那真是该死的接近@Bakuriu的回答。