Two Tuple Keys
我建议放弃嵌套字典的想法,直接使用两个元组作为键。就像这样:
d = { (user_id, analysis_type_id): count for count, analysis_type_id, user_id in counts}
字典是一个哈希表。在Python中,每个二元组有一个单一的哈希值(而不是两个哈希值),因此每个二元组都是基于其(相对)唯一的哈希进行查找的。因此,这比查找两个单独键的哈希值(首先是
user_id
,然后是
analysis_type_id
)要快(大多数情况下快2倍)。
但是,
注意过早优化。除非您正在执行数百万次查找,否则扁平
dict
的性能提高不太可能会有影响。支持使用两个值的语法和可读性更好的原因是,假设大多数时间您将根据一对值访问项目,而不是基于单个值的项目组。
考虑使用
namedtuple
创建命名元组以存储这些键可能很方便。可以这样做:
from collections import namedtuple
IdPair = namedtuple("IdPair", "user_id, analysis_type_id")
然后在您的字典推导式中使用它:
d = { IdPair(user_id, analysis_type_id): count for count, analysis_type_id, user_id in counts}
您可以像这样访问您感兴趣的计数:
somepair = IdPair(user_id = 4, analysis_type_id = 1)
d[somepair]
这种情况有时很有用,因为你可以做这样的事情:
user_id = somepair.user_id
其他有用的选项
上述解决方案的一个缺点是查找失败的情况。在这种情况下,您只会得到以下类似的回溯信息:
>>> d[IdPair(0,0)]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: IdPair(user_id=0, analysis_type_id=0)
这并不是很有帮助。是
user_id
没有匹配,还是
analysis_type_id
没有匹配,还是两者都没有匹配?
您可以通过创建自己的
dict
类型来为自己创建更好的工具,以便获得更多信息和漂亮的回溯。它可能看起来像这样:
class CountsDict(dict):
"""A dict for storing IdPair keys and count values as integers.
Provides more detailed traceback information than a regular dict.
"""
def __getitem__(self, k):
try:
return super().__getitem__(k)
except KeyError as exc:
raise self._handle_bad_key(k, exc) from exc
def _handle_bad_key(self, k, exc):
"""Provides a custom exception when a bad key is given."""
try:
user_id, analysis_type_id = k
except:
return exc
has_u_id = next((True for u_id, _ in self if u_id==user_id), False)
has_at_id = next((True for _, at_id in self if at_id==analysis_type_id), False)
exc_lookup = {(False, False):KeyError(f"CountsDict missing pair: {k}"),
(True, False):KeyError(f"CountsDict missing analysis_type_id: "
f"{analysis_type_id}"),
(False, True):KeyError(f"CountsDict missing user_id: {user_id}")}
return exc_lookup[(user_id, analysis_type_id)]
像普通的 dict
一样使用它。
然而,当你尝试访问缺失的键值对时,简单地向你的 dict
添加新的键值对(计数为零)可能更有意义。如果是这种情况,我会使用一个 defaultdict
,并在访问缺失的键时将计数设置为零(使用 int
的默认值作为工厂函数)。就像这样:
from collections import defaultdict
my_dict = defaultdict(default_factory=int,
((user_id, analysis_type_id), count) for count, analysis_type_id, user_id in counts))
现在,如果您尝试访问缺失的键,则计数将被设置为零。但是,
这种方法的一个问题是,所有键都将被设置为零:
value = my_dict['I'm not a two tuple, sucka!!!!'] # <-- will be added to my_dict
为了避免这种情况,我们回到制作一个
CountsDict
的想法,但在这种情况下,您的特殊
dict
将是
defaultdict
的子类。然而,与常规的
defaultdict
不同的是,在添加之前它将检查键是否是有效的类型。作为奖励,我们可以确保任何成对元组添加为键时都变成了一个
IdPair
。
from collections import defaultdict
class CountsDict(defaultdict):
"""A dict for storing IdPair keys and count values as integers.
Missing two-tuple keys are converted to an IdPair. Invalid keys raise a KeyError.
"""
def __getitem__(self, k):
try:
user_id, analysis_type_id = k
except:
raise KeyError(f"The provided key {k!r} is not a valid key.")
else:
k = IdPair(user_id, analysis_type_id)
return super().__getitem__(k)
使用它就像普通的defaultdict
一样:
my_dict = CountsDict(default_factory=int,
((user_id, analysis_type_id), count) for count, analysis_type_id, user_id in counts))
注意:上述代码中,我没有将两个元组键转换为IdPair
,因为在实例创建时不使用__setitem__
。要创建此功能,我们还需要实现__init__
方法的覆盖。
总结
在所有这些选项中,更有用的选项完全取决于您的用例。
mydict[(4,1)] = 4
。 - Rick