如何通过* args参数获取dict中任何深度的值? (蟒蛇)

时间:2013-09-26 09:40:40

标签: python dictionary

例如,我在这里:

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

2 个答案:

答案 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

即。它“累积”值,在给定的参数上调用二进制函数。 在您的情况下,首先adoc。然后,它成为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的回答。