Python 二维字典或其他数据结构,其中键的顺序不为';没关系

Python 二维字典或其他数据结构,其中键的顺序不为';没关系,python,dictionary,data-structures,Python,Dictionary,Data Structures,我想创建一个数据结构,它可以接受一对键(字符串)并返回一个值(以及字符串)。无论输入两个键的顺序如何,我都希望返回相同的值(例如,数据[key1][key2]返回与数据[key2][key1]相同的值)。该描述是否有术语/概念 我当前的实现是创建一个2D字典,如下所示: my_dict = {'key1': {'key1': None, 'key2': 'foo', ... '

我想创建一个数据结构,它可以接受一对键(字符串)并返回一个值(以及字符串)。无论输入两个键的顺序如何,我都希望返回相同的值(例如,数据[key1][key2]返回与数据[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个条目

因此,我尝试创建一个1D字典,其中键是元组:

my_dict = {('key1', 'key2'): 'foo'}
但这不起作用,因为调用
my_dict[('key2','key1')]
会给我一个
keyrorm

1D元组字典的一个解决方法是创建try/except

def get_value(my_dict, key1, key2):
    try:
        return my_dict[key1][key2]
    except KeyError:
        return my_dict[key2][key1]
这看起来并不直观,更像是解决问题的“创可贴”


我没有测试过的一个方法是1D字典,其中键使用一个自定义定义类的实例,该类将
key1
key2
作为属性。为了做到这一点,对象必须是不可交换和可散列的,其中散列函数将使用对象的属性并生成相同的“散列键”,而不管属性的顺序如何。我以前从未这样做过,也不知道怎么做。这样做对吗?我觉得自己很愚蠢,因为我没能弄明白这一点,因为似乎有一个简单的答案。

如果你想让键在不考虑顺序的情况下进行相等的比较,你可以使用
冻结集
作为键,这符合你对自定义类的想法:

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
即使对frozenet的创建和两个元素的查找计时也会更快:

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个,所以对于任何比一对夫妇更重要的事情,检查每一个可能的排列都是不现实的,或者根本没有效率的

怎么样

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'
正如你所建议的,你可以使用一个物体。要实现这一点,您需要实现
\uuuuuuuuuuuuhash\uuuuuuu
\uuuuuuuueq\uuuuuu
\uuuuuucmp\uuuuuuuuuuuuuuuuuuuuuuuuuuuu(两种方法之一),如下所示:

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

这是我发现的。列表的维度必须相同

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可能更好。

如果元组中有几个元素,则必须手动检查每个元素combination@PadraicCunningham不,这将返回
my_dict[(key2,key1)]
或者,如果它不存在,它将返回
my_dict[(key1,key2)]
@Delgan,是的,我错过了参数,还是不太实用的两个以上elements@PadraicCunningham在这种情况下,元组只有2个值是固定的。我假设提问者实际上是在用键而不是索引生成一个2D矩阵。@YashMehrotra,你有n!排序键的方法,因此即使是3个键,也不是您真正想要考虑的事情,使用if/else检查也会比使用get快。如果我决定要更多的键,这是go-to方法吗(例如,从6个键中获取值,顺序不重要)?确定。您甚至可以实现任意
N
元素。虽然在这种情况下,我没有看到比建议的
frozenset
解决方案有什么好处,所以定制实现实际上毫无意义。我经常做的是创建一个类型别名,例如
Key=frozenset
。如果以后需要对类进行更改,则可以更改,更不用说可以为对象提供更有意义的名称。您可以像通常一样实例化,例如
k=Key()
Gotcha。因此,我首先需要从我的数据创建新的字典,这将需要我为键对创建一个
frozenset
。然后,当我想在字典中查找某些内容时,我必须使用我的输入键创建一个新的
冻结集
,并使用它来获取我的值。这似乎是迄今为止我找到的最佳解决方案。感谢您向我展示
frozenset
@koreebay,frozenset是不可变的,因此它们可以用作dict键,如果您的内部有1个或101个不同的字符串,它们将起作用。但是,这对键将始终作为字符串提供。如果每次我想查字典的时候都要从两个字符串中创建一个新的
frozenset
键,那么这仍然可行吗?@koreebay,是的,它会很好地工作,如果你让里面的字符串数量增加,它也会比尝试每一个排列都有很大的性能,即使有3把钥匙,你也有6次烫发,即3次!要考虑每一次,对于6个字符串,你有720个排列要考虑,这样你就可以看到它在尺度上有多好。