如何在 Python 的字典中请求子键的键?

How can I ask for a key of a subkey in a dictionary in Python?

如果我有一个字典中的一个字典,我如何在恒定时间内请求一个键? 例如:

def get_hobby(hobby):
    d = {'An' : {'Hobby': "Paintball", 'Age' : 22}, 'Jef' : {'Hobby' : "Football", 'Age': 24}, 'Jos' : {'Hobby': "Paintball", 'Age' : 46}}
assert get_hobby("Paintball") == ['An', 'Jos']

这行不通:

return d.keys[hobby]

这应该有效:

return [key for key, val in d.items() if val['Hobby'] == hobby]

例如:

def get_hobby(hobby):
    d = {
        'An': {'Hobby': "Paintball", 'Age' : 22},
        'Jef': {'Hobby' : "Football", 'Age': 24},
        'Jos' : {'Hobby': "Paintball", 'Age' : 46}
    }
    return [key for key, val in d.items() if val['Hobby'] == hobby]

print get_hobby("Paintball")

结果:

['Jos', 'An']

使用列表理解:

return [name for name, props in d.items() if props['Hobby'] == hobby]

d.items() 给你一个 (key, value) 对的序列,其中值是嵌套字典。列表理解通过将 hobby 变量与嵌套的 'Hobby' 键匹配来过滤这些,生成过滤器测试 returns True.[=20 的名称列表=]

你不能在常数时间内要求钥匙,因为那个数字是可变的。

演示:

>>> def get_hobby(hobby):
...     d = {'An' : {'Hobby': "Paintball", 'Age' : 22}, 'Jef' : {'Hobby' : "Football", 'Age': 24}, 'Jos' : {'Hobby': "Paintball", 'Age' : 46}}
...     return [name for name, props in d.items() if props['Hobby'] == hobby]
... 
>>> get_hobby("Paintball")
['Jos', 'An']

请注意,returned 键列表是任意顺序,因为字典没有固定顺序。您不能简单地针对另一个列表测试该列表并期望它每次都相等,因为列表确实有顺序。具体顺序取决于Python hash seed和字典的插入和删除历史。

您可能想要 return 一个 设置 ;集合也没有顺序,更好地反映了匹配键的性质 returned:

return {name for name, props in d.items() if props['Hobby'] == hobby}

之后您的断言将变为:

assert get_hobby("Paintball") == {'An', 'Jos'}

如果您需要在恒定时间内进行大量此类查询,则必须更改为适当的数据结构。例如:

d2 = {}
for name, subdict in d.items():
    for key, value in subdict:
        d2.setdefault((key, value), set()).add(name)

(请注意,我使用了 set,而不是 list 解释了原因。)

现在:

d2['Hobby', 'Paintball']

简单、高效。

当然,构建数据结构并不需要常数时间;它显然必须遍历整个字典中每个元素的每个子元素。但是你只做一次,然后你所有的不计其数的查询都是恒定的时间。所以,只要你买得起space,而"zillion"其实是一个很大的数字,这就是你想要的优化。

您将需要重组您的代码,这样 dict 实际上 构建一次,而不是每次 get_hobbies 被调用。这是否意味着将它放在 class 中,使用闭包,显式记忆存储在函数上的属性,或者只使用在顶层构建的全局变量,由您决定。拿最后一个,只因为它最短(可能不是最好的):

d = {'An' : {'Hobby': "Paintball", 'Age' : 22}, 'Jef' : {'Hobby' : "Football", 'Age': 24}, 'Jos' : {'Hobby': "Paintball", 'Age' : 46}}
d2 = {}
for name, subdict in d.items():
    for key, value in subdict:
        d2.setdefault((key, value), set()).add(name)

def get_hobby(hobby):
    return d2['Hobby', hobby]

assert get_hobby("Paintball") == {'An', 'Jos'}