【问题标题】:Multiples-keys dictionary where key order doesn't matter键顺序无关紧要的多键字典
【发布时间】:2017-10-08 15:54:10
【问题描述】:

我正在尝试创建一个以两个字符串作为键的字典,并且我希望键按任意顺序排列。

myDict[('A', 'B')] = 'something'
myDict[('B', 'A')] = 'something else'
print(myDict[('A', 'B')])

我希望这段代码打印“其他东西”。不幸的是,似乎顺序与元组很重要。用作键的最佳数据结构是什么?

【问题讨论】:

  • 你为什么希望它打印出来? ('A', 'B') 映射到 something
  • 如果你想让它打印something else然后试试print(myDict[('B','A')])
  • 嗯,是的,我没想到,但我想使用元组以外的数据结构来做到这一点。
  • 那它怎么知道是选择something还是something else呢?键必须是唯一的
  • 换句话说 - Python 中没有办法让数据结构省略元素排序?

标签: python dictionary


【解决方案1】:

使用frozenset

您可以使用frozenset,而不是有序的tuple,它是无序的,而frozenset 仍然是可散列的 >不可变的。

myDict = {}
myDict[frozenset(('A', 'B'))] = 'something'
myDict[frozenset(('B', 'A'))] = 'something else'
print(myDict[frozenset(('A', 'B'))])

将打印的内容:

something else


不幸的是,这种简单性有一个缺点,因为frozenset 基本上是一个“冻结”集合。 frozenset 中不会有重复值,例如,

frozenset((1, 2)) == frozenset((1,2,2,1,1))

如果缩减值不打扰您,请随时使用frozenset

但是,如果您 100% 确定不希望发生上述情况,则有两种替代方法:


第一个方法是使用Counter,然后再次使用frozenset使其成为hashable:(注意:元组中的所有内容都必须是可散列的)

from collections import Counter

myDict = {}
myDict[frozenset(Counter(('A', 'B')).items())] = 'something'
myDict[frozenset(Counter(('B', 'A')).items())] = 'something else'
print(myDict[frozenset(Counter(('A', 'B')).items())])

# something else

第二种方法是使用内置函数sorted,并通过将其设为tuple使其hashable。这将在用作键之前对值进行排序:(注意:元组中的所有内容都必须是 sortable 和 hashable)

myDict = {}
myDict[tuple(sorted(('A', 'B')))] = 'something'
myDict[tuple(sorted(('B', 'A')))] = 'something else'
print(myDict[tuple(sorted(('A', 'B')))])

# something else

但是如果元组元素既不是所有的hashable,也不是所有的sortable,不幸的是,你可能不走运,需要创建自己的dict结构。 .. D:

【讨论】:

  • 您的frozenset(Counter(…)) 代码错误:frozenset(Counter('abc'))、frozenset(Counter('abbccc')) 和 frozenset(('a', 'b', 'c')) 都相等。你想要frozenset(Counter(xs).iteritems())(Python 3 中的.items())。
  • 另外,如果元素不可散列,那么tuple(sorted(xs)) 仍然不是有效的键,因为它本身不是可散列的:例如,如果xs == [[1], [2]],则@987654347 @,当您将其用作键时也会产生错误。听起来你想说的是json.dumps(xs, sort_keys=True),但老实说,我无法想象每次你想在字典中查找活动对象时将它们编组到键的情况是个好主意。
【解决方案2】:

您可以构建自己的结构:

class ReverseDict:
   def __init__(self):
      self.d = {}
   def __setitem__(self, k, v):
      self.d[k] = v

   def __getitem__(self, tup):
      return self.d[tup[::-1]]

myDict = ReverseDict()
myDict[('A', 'B')] = 'something'
myDict[('B', 'A')] = 'something else'
print(myDict[('A', 'B')])

输出:

something else

【讨论】:

    【解决方案3】:

    我认为这里的重点是元组的元素无论它们的顺序如何都指向同一个字典元素。这可以通过使哈希函数在元组键元素上可交换来完成:

    class UnorderedKeyDict(dict):
        def __init__(self, *arg):
            if arg:
                for k,v in arg[0].items():
                    self[k] = v
    
        def _hash(self, tup):
            return sum([hash(ti) for ti in tup])
    
        def __setitem__(self, tup, value):
            super().__setitem__(self._hash(tup), value)
    
        def __getitem__(self, tup):
          return super().__getitem__(self._hash(tup))
    
    mydict = UnorderedKeyDict({('a','b'):12,('b','c'):13})
    mydict[('b','a')]
        >> 12
    

    【讨论】:

      猜你喜欢
      • 2021-10-24
      • 2022-01-25
      • 2023-03-05
      • 2021-06-11
      • 1970-01-01
      • 1970-01-01
      • 2020-09-10
      • 1970-01-01
      相关资源
      最近更新 更多