Python“三胞胎”字典?

时间:2019-04-28 16:58:42

标签: python

如果我们有(a1, b1)(a2, b2),很容易使用字典来存储对应关系:

dict[a1] = b1
dict[a2] = b2

我们可以毫无问题地得到(a1, b1)(a2, b2)

但是,如果我们有(a1, b1, c1)(a2, b2, c2),是否有可能得到类似的东西:

dict[a1] = (b1, c1)
dict[b1] = (a1, c1)

在哪里可以使用a1b1来取回三元组(a1, b1, c2)?那有意义吗?我不太确定该问题要使用哪种数据类型。上面的方法可以工作,但是会有重复的数据。

基本上,如果我有一个三元组,我可以使用哪种数据类型,以便可以使用第一个或第二个值来取回三元组?

5 个答案:

答案 0 :(得分:10)

解决方案

您可以编写自己的映射数据结构,以允许add进行三元组或任意大小的组,并使用__getitem__恢复该组。

class GroupMap:
    def __init__(self):
        self.data = {}

    def add(self, group):
        for item in group:
            self.data[item] = group

    def __getitem__(self, item):
        return self.data[item]

group = (1, 2, 3)
group_map = GroupMap()

group_map.add(group)

print(group_map[1]) # (1, 2, 3)

请注意,此GroupMap可以用于任何大小的组,而不仅仅是三组。

上面的下一步是根据发生碰撞时想要的行为来扩展类以避免碰撞。

理论

您可能想知道是否有更好的方法来表示一组连接的对象。答案不是真的。

假设您有一个包含{em> n 个顶点的graph。然后,要连接图形,您必须至少具有 n-1 条边。在上面的数据结构中,我在dict中使用了 n 条目,这意味着该解决方案几乎是最优的。

为什么可以使用 n-1 条目?因为您随后需要遍历所有图形才能恢复整个组。因此,再使用一条边可以进行 O(1)查找,这可能是您可能要权衡的。

答案 1 :(得分:2)

如果您想继承dict(以获得与dict相关的所有其他方法,例如.get和whatnot)的替代方法,并且仅 获得其他元素(由于某些原因)。您可以制作一本属于您自己的新词典

class TupleDict(dict):

    def __setitem__(self, key, value):
        assert isinstance(key, tuple)
        for i, e in enumerate(key):
            dict.__setitem__(self, e, key[:i] + key[i+1:] + (value,))
        dict.__setitem__(self, value, key)

,然后将任何元组键分配给单个值(不确定我喜欢这种语法,但是我们可以使其不同或使用独立方法)

d = TriDict()
d[(1,2)] = 4

,您将得到__getitem__的结果,返回不存在的其余元组。

>>> print(d[1])
(2, 4)
>>> print(d[2])
(1, 4)
print(d[4])
>>> (1, 2)

答案 2 :(得分:1)

字典只能存储键值对。

您可以使用运算符重载来制作自己的三元组字典,这样当您用三元组的任何成员建立索引时,您会得到另外两个三元组,也许是这样的:

class trictionary:
    def __init__(self):
        self.data = []

    def add(self, group):
        self.data.append(group)

    def __getitem__(self, key):
        for group in data: #Find the set the key belongs to.
            if key in group:
                return tuple(group)

这避免了复制数据,并具有以性能为代价寻找的功能。也许有更好的方法可以做到这一点。

答案 3 :(得分:1)

基于OlivierMelançons的答案,我想出了这一点-万一值在元组中的位置很重要:

class GroupMap:
    def __init__(self, data=None):
        self.data = {}
        if data:
            self.add(data)

    def add(self, data):
        for idx, key in enumerate(data):
            self.data.setdefault(idx, {})[key] = data

    def __getitem__(self, key):
        # lookup in first index
        return self.getby(0, key)

    def getby(self, idx, key):
        return self.data[idx].get(key)


data = ('a', 'b', 'c')
g = GroupMap(data)
more_data = ('b', 'a', 'z')
g.add(more_data)

assert g['a'] == data

assert g.getby(0, 'a') == data
assert g.getby(0, 'b') == more_data
assert g.getby(0, 'c') is None

assert g.getby(1, 'a') == more_data
assert g.getby(1, 'b') == data

assert g.getby(2, 'c') == data
assert g.getby(2, 'z') == more_data

assert id(data) == id(g['a']) == id(g.getby(1, 'b'))

答案 4 :(得分:0)

您的问题中的示例与主要问题有所出入:

  

基本上,如果我有一个三元组,我可以使用哪种数据类型,以便可以使用第一个或第二个值来取回三元组?

一个字典。分配键值对elementtriplet(请参见@OlivierMelançon的答案):

代码

d = {}
for x in triplet:
   d[x] = triplet

演示

d["a"]
# ('a', 'b', 'c')

d["b"]
# ('a', 'b', 'c')

d["c"]
# ('a', 'b', 'c')

OP要求明确以下方面的首选行为:

  • 添加元素,例如d[a1] = (b1, c1)f((a1, b1, c1))
  • 元素排序,例如(a1, b1, c1)(b1, a1, c1)
  • 重复数据,例如按住(a1, b1, c1) 3次或2元组子集的组合

解决了这些问题,就有可能提出更详尽的解决方案。