我想创建一个数据结构,它可以接受一对键(字符串)并返回一个值(字符串也是如此)。无论输入2个键的顺序如何,我都希望返回相同的值(例如,data [key1] [key2]返回与data [key2] [key1]相同的值)。这个描述有一个术语/概念吗?
我目前的实现是创建一个像这样的2D字典:
my_dict = {'key1': {'key1': None,
'key2': 'foo',
...
'keyn': 'bar'},
'key2': {'key1': 'foo',
'key2': None,
...
'keyn': 'baz'},
...
'keyn': {'key1': 'bar',
'key2': 'baz',
...
'keyn': None}}
# Calling my_dict['key1']['key2'] and my_dict['key2']['key1'] both return 'foo', which is what I want and expect.
这对我来说似乎不对。我正在复制数据,当我只需要(n *(n - 1))/ 2时,我正在创建n * n个条目。
所以,我尝试创建一个字典,其中键是一个元组:
my_dict = {('key1', 'key2'): 'foo'}
但这不起作用,因为调用my_dict[('key2', 'key1')]
会给我一个KeyError
1D元组词典的一个解决方法是创建一个try / except。
def get_value(my_dict, key1, key2):
try:
return my_dict[key1][key2]
except KeyError:
return my_dict[key2][key1]
这似乎不直观,感觉更像是对这个问题的“创可贴”。
我还没有测试过的一种方法是一维字典,其中密钥使用一个自定义类的实例,该类将key1
和key2
作为属性。为了做到这一点,对象必须是不可变的和可散列的,其中散列函数将使用对象的属性并产生相同的“散列键”而不管属性的顺序如何。我以前从未这样做过,也不知道该怎么做。这是正确的方法吗?我觉得非常愚蠢,我无法弄清楚这一点,因为似乎有一个简单的答案。
答案 0 :(得分:2)
如果您希望无论顺序如何都能比较相等的键,您可以使用frozensets
作为符合您自定义类的想法的键:
my_dict = {frozenset(['key1', 'key2']): 'foo'}
添加密钥的顺序无关紧要:
In [44]: my_dict = {frozenset(['key1', 'key2']): 'foo'}
In [45]: k = frozenset(["key1","key2"])
In [46]: k2 = frozenset(["key2","key1"])
In [47]: my_dict[k]
Out[47]: 'foo'
In [48]: my_dict[k2]
Out[48]: 'foo'
您可以在冻结集中包含尽可能多的值,它们仍然可以比较相等,使用冻结集对查找也很有效:
In [55]: timeit my_dict[k]
10000000 loops, best of 3: 103 ns per loop
In [56]: timeit get_value(my_dict, 'key1', 'key2')
1000000 loops, best of 3: 455 ns per loop
In [57]: timeit get_value(my_dict, 'key2', 'key1')
1000000 loops, best of 3: 455 ns per loop
甚至计时冻结网络创建和两个元素的查找速度更快:
In [5]: my_dict = {frozenset(['key1', 'key2']): 'foo'}
In [6]: timeit my_dict[frozenset(["key1","key2"])]
1000000 loops, best of 3: 380 ns per loop
只有3个字符串,你有3个!要检查的权限,对于6你有720所以对于任何超过一对检查每个可能的排列是不现实或远程有效。
答案 1 :(得分:1)
您可以按照建议使用hashable对象。为此,您需要实现__hash__
,__eq__
或__cmp__
(两种方法之一)方法,如下所示:
class Key:
def __init__(self, key1, key2):
self.key1 = key1
self.key2 = key2
def __hash__(self):
# XORing two hash values is usually fine. Besides, the operation is symmetric, which is what you want
return hash(self.key1) ^ hash(self.key2)
def __eq__(self, other):
if self == other:
return True
if self.key1 == other.key1 and self.key2 == other.key2:
return True
if self.key1 == other.key2 and self.key2 == other.key1:
return True
return False
答案 2 :(得分:0)
怎么样
my_dict = {('key1', 'key2'): 'foo'}
def get_value(my_dict, key1, key2):
return my_dict.get((key2, key1) , my_dict.get((key1, key2)))
这样,您必须减少条目,并且优于try/except
实施例
In [11]: my_dict = {('key1', 'key2'): 'foo'}
In [12]: def get_value(my_dict, key1, key2):
....: return my_dict.get((key2, key1) , my_dict.get((key1, key2)))
In [13]: get_value(my_dict, 'key1', 'key2')
Out[13]: 'foo'