使用 set 作为对可迭代对象重新排序的一种方式



我有两个元组ab,还有一个字典d

a=('C', 'G')
b=('G', 'C') 
d={('G', 'C'): 'T'}

我希望d[a]d[b]'T'返回.

为此,我使用了set()因为我认为set()正在修复可迭代对象元素的顺序(并删除重复项,但我不在乎(。

所以我做到了:

tuple(set(a)) in d  # return True
tuple(set(b)) in d  # return True
# I did tuple(set()) because set() alone is not hashable and so cannot be used directly

这有效,直到它不起作用。所以我发现比有时

set(('C', 'G'))
{'C', 'G'}

有时它不会

set(('C', 'G'))
{'G', 'C'}

我知道set()按照它想要的方式对元素进行排序,但我希望给定元素集的顺序始终相同。

所以我的问题是:

  • 为什么不是这样?
  • 如何解决我最初的问题?

每当需要索引字典时,只需使用排序元组:

a = ('C', 'G')
b = ('G', 'C')
d = {tuple(sorted(a)): 'T'}
for tup in [a, b]:
print( tup, d[tuple(sorted(tup))] )

该集合建立在某种数据结构之上,该结构允许对成员进行有效的检查。 结果是,Python 在实例化此结构时会丢失代码中指定的原始顺序。

如果您真的只有两个标签,我可能只会显式指定这两个值。

a=('C', 'G')
b=('G', 'C') 
d={a: 'T', b: 'T'}

要解决此问题的更大版本,您需要分别设置两个订单的值。 对于更大的问题,我会使用双循环的理解

lbls = ['a', 'b', 'c']
{(f, s):'T' for f in lbls for s in lbls if f != s}

一种解决方案是通过对键进行排序来规范化键。当我们抬头时,我们也会在抬头之前对键进行排序。

import collections.abc
def _normalize(key):
""" Normalize a key (tuple) by sorting """
key = tuple(sorted(key))
return key
class TupleKeyDict(collections.abc.MutableMapping):
def __init__(self, *args, **kwargs):
temp_dict = dict(*args, **kwargs)
self.dict = {
_normalize(key): value
for key, value in temp_dict.items()
}
def __getitem__(self, key):
value = self.dict[_normalize(key)]
return value
def __setitem__(self, key, value):
self.dict[_normalize(key)] = value
def __delitem__(self, key):
del self.dict[_normalize(key)]
def __iter__(self):
return iter(self.dict)
def __len__(self):
return len(self.dict)
def __repr__(self):
return repr(self.dict)
>>> tkd = TupleKeyDict(d)
>>> tkd
{('C', 'G'): 'T'}
>>> tkd['C', 'G']
'T'
>>> tkd['G', 'C']
'T'
>>> tkd.get(('X', 'Y')) is None
True
>>> tkd['X', 'C'] = 'cx'
>>> tkd['C', 'X']
'cx'

讨论

我们不能在集合可变(可变(之前使用集合作为键,因此没有常量哈希 - 成为键的要求。我的方法是创建一个专门的字典,其中键是排序元组,这使得查找更容易。

__init__方法接收您可以传递到dict()的所有内容。__repr__方法有助于调试。所有其他方法都可以满足collections.abc.MutableMapping要求。

最新更新