[英]2D Dictionary or other data structure where order of keys doesn't matter
I want to create a data structure that can take in a pair of keys (strings) and return a value (string as well). 我想创建一个数据结构,可以接受一对键(字符串)并返回一个值(字符串也是如此)。 I would like to return the same value regardless of the order in which the 2 keys are entered (eg data[key1][key2] returns the same value as data[key2][key1]).
无论输入2个键的顺序如何,我都希望返回相同的值(例如,data [key1] [key2]返回与data [key2] [key1]相同的值)。 Is there a term/concept for this description?
这个描述有一个术语/概念吗?
My current implementation is to create a 2D dictionary like this: 我目前的实现是创建一个像这样的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.
This doesn't seem right to me. 这对我来说似乎不对。 I am duplicating data, and I am creating n * n entries when I only need (n * (n - 1))/2.
我正在复制数据,当我只需要(n *(n - 1))/ 2时,我正在创建n * n个条目。
So, I tried creating a 1D Dictionary where the key is a tuple: 所以,我尝试创建一个字典,其中键是一个元组:
my_dict = {('key1', 'key2'): 'foo'}
But this doesn't work as calling my_dict[('key2', 'key1')]
gives me a KeyError
但这不起作用,因为调用
my_dict[('key2', 'key1')]
给了我一个KeyError
One work-around for the 1D tuple Dictionary is to create a try/except. 1D元组字典的一个解决方法是创建一个try / except。
def get_value(my_dict, key1, key2):
try:
return my_dict[key1][key2]
except KeyError:
return my_dict[key2][key1]
This doesn't seem intuitive and feels more like a 'band-aid' to the problem. 这似乎不直观,感觉更像是对这个问题的“创可贴”。
One method I haven't tested is a 1D Dictionary where the key uses an instance of a custom-defined class that holds key1
and key2
as attributes. 我没有测试过的一种方法是一维字典,其中密钥使用自定义类的实例,该类将
key1
和key2
保存为属性。 In order to do this, the object would have to be nonmutable and hashable, where the hash function would use the object's attributes and produce the same "hash key" regardless of the order of attributes. 为了做到这一点,对象必须是不可变的和可散列的,其中散列函数将使用对象的属性并产生相同的“散列键”而不管属性的顺序如何。 I've never done this before and don't know how to do this.
我以前从未这样做过,也不知道该怎么做。 Is this the right way to go about it?
这是正确的方法吗? I feel very stupid that I haven't been able to figure this out, as it seems like there is an easy answer to this.
我觉得非常愚蠢,我无法弄清楚这一点,因为似乎有一个简单的答案。
If you want the keys to compare equal regardless of order, you could use frozensets
as keys which fits in with your idea of a custom class: 如果您希望无论顺序如何都能比较相等的键,您可以使用
frozensets
作为符合您自定义类的想法的键:
my_dict = {frozenset(['key1', 'key2']): 'foo'}
Does not matter what order you add the keys: 无论您添加密钥的顺序如何:
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'
You can have as many values in the frozenset as you want they will still compare equal, using a frozen set is also efficient for lookups: 您可以在冻结集中拥有尽可能多的值,它们仍然可以比较相等,使用冻结集对查找也很有效:
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
Even timing the frozenet creation and the lookup for two elements is faster: 甚至计时冻结创建和查找两个元素的速度更快:
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
For just 3 strings you have 3! 只有3个字符串,你有3个! perms to check, for 6 you have 720 so for anything more than a couple checking every possible permutation is not realistic or remotely efficient.
要检查的权限,对于6你有720所以对于任何超过一对检查每个可能的排列是不现实或远程有效。
You could use an hashable object, as you suggest. 您可以按照建议使用可清除对象。 To achieve this you would need to implement
__hash__
, and __eq__
or __cmp__
(one of the two) methods, like so: 要做到这一点,你需要实现
__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
How about 怎么样
my_dict = {('key1', 'key2'): 'foo'}
def get_value(my_dict, key1, key2):
return my_dict.get((key2, key1) , my_dict.get((key1, key2)))
This way, you have to make less entries, and its better than try/except
这样,你必须减少输入,并且比
try/except
更好
Example 例
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'
Here's what I found. 这是我发现的。 The dimensions of the lists have to be the same.
列表的尺寸必须相同。
my_dict = {}
sub_dict = {}
ks = ['key1','key2','key3','keyn']
kks = ['key1','key2','key3','keyn']
vals = [None,'foo','bar','baz']
for val in vals:
for kk in kks:
sub_dict[kk] = val
print sub_dict
for k in ks:
my_dict[k] = sub_dict
print my_dict
Frozenset is probably better. Frozenset可能更好。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.