对称字典,其中d [a] [b] == d [b] [a]

时间:2010-12-06 16:11:39

标签: python inheritance dictionary

我在python中有一个算法,它为值对创建度量,其中m(v1, v2) == m(v2, v1)(即它是对称的)。我有想法写一个词典字典,其中这些值以一种以内存有效的方式存储,以便可以使用任何顺序的键轻松检索它们。我喜欢从事物中继承,理想情况下,我喜欢写symmetric_dict,其中s_d[v1][v2]总是等于s_d[v2][v1],可能是根据某种类型检查哪个v更大。排序关系,然后切换它们,以便始终首先提到较小的元素。即,在调用s_d[5][2] = 4时,dicts的dict将转向它们,以便它们实际上存储为s_d[2][5] = 4,并且用于检索数据。 我也非常愿意接受更好的数据结构,但我更喜欢一种与“is-a”关系的实现,它只使用一个dict并预处理一些函数参数。

7 个答案:

答案 0 :(得分:11)

您可以使用frozenset作为词典的关键词:

>>> s_d = {}
>>> s_d[frozenset([5,2])] = 4
>>> s_d[frozenset([2,5])]
4

编写dict的子类,将iterables作为关键参数,然后在存储值时转为frozenset,这是相当简单的:

class SymDict(dict):
    def __getitem__(self, key):
        return dict.__getitem__(self, frozenset(key))

    def __setitem__(self, key, value):
        dict.__setitem__(self, frozenset(key), value)

这给了你:

>>> s_d = SymDict()
>>> s_d[5,2] = 4
>>> s_d[2,5]
4

答案 1 :(得分:3)

如图所示使用嵌套索引进行操作将非常困难。最好使用元组作为密钥。这样就可以对元组进行排序,并且可以访问封装的dict作为值。

d[2, 5] = 4
print d[5, 2]

答案 2 :(得分:2)

作为Dave Webb冻结的替代品,为什么不做以下的SymDict:

class SymDict(dict):
    def __getitem__(self, key):
        return dict.__getitem__(self, key if key[0] < key[1] else (key[1],key[0]))

    def __setitem__(self, key, value):
        dict.__setitem__(self, key if key[0] < key[1] else (key[1],key[0]), value)

通过快速测试,获取和设置项目的速度比使用冷冻集快10%。无论如何,只是另一个想法。但是,它的适应性低于冷冻集,因为它实际上只设置为与长度为2的元组一起使用。据我所知,OP在这里似乎不是问题。

答案 3 :(得分:2)

这是一个看起来很有前景的略有不同的方法。虽然SymDict类不是dict子类,但它大多表现为一个,并且只涉及一个私有字典。我认为一个有趣的特性是,它保留了您似乎想要的自然[][]查找语法。

class SymDict(object):
    def __init__(self, *args, **kwrds):
        self._mapping = _SubSymDict(*args, **kwrds)
    def __getitem__(self, key1):
        self._mapping.set_key1(key1)
        return self._mapping
    def __setitem__(self, key1, value):
        raise NotImplementedError
    def __str__(self):
        return '_mapping: ' + self._mapping.__str__()
    def __getattr__(self, name):
        return getattr(self._mapping, name)

class _SubSymDict(dict):
    def __init__(self, *args, **kwrds):
        dict.__init__(self, *args, **kwrds)
    def set_key1(self, key1):
        self.key1 = key1
    def __getitem__(self, key2):
        return dict.__getitem__(self, frozenset((self.key1, key2)))
    def __setitem__(self, key2, value):
        dict.__setitem__(self, frozenset((self.key1, key2)), value)

symdict = SymDict()
symdict[2][4] = 24
symdict[4][2] = 42

print 'symdict[2][4]:', symdict[2][4]
# symdict[2][4]: 42
print 'symdict[4][2]:', symdict[4][2]
# symdict[4][2]: 42
print 'symdict:', symdict
# symdict: _mapping: {frozenset([2, 4]): 42}

print symdict.keys()
# [frozenset([2, 4])]

答案 4 :(得分:2)

改进Justin Peel的解决方案,您需要添加__delitem____contains__方法才能使更多字典操作正常工作。所以,为了完整,

class SymDict(dict):
    def __getitem__(self, key):
        return dict.__getitem__(self, key if key[0] < key[1] else (key[1],key[0]))

    def __setitem__(self, key, value):
        dict.__setitem__(self, key if key[0] < key[1] else (key[1],key[0]), value)

    def __delitem__(self, key):
        return dict.__delitem__(self, key if key[0] < key[1] else (key[1],key[0]))

    def __contains__(self, key):
        return dict.__contains__(self, key if key[0] < key[1] else (key[1],key[0]))

那么

>>> s_d = SymDict()
>>> s_d[2,5] = 4
>>> s_d[5,2]
4
>>> (5,2) in s_d
True
>>> del s_d[5,2]
>>> s_d
{}
但是,我不确定是否涵盖所有基础,但它对我自己的代码来说已经足够了。

答案 5 :(得分:1)

一个明显的替代方法是使用(v1,v2)元组作为单个标准dict的密钥,并将(v1,v2)(v2,v1)插入字典中,使它们成为< em>在右侧引用相同的对象。

答案 6 :(得分:1)

我将提取函数以提高可读性(对于patvarilly回答)

class SymDict(dict):
def __getitem__(self, key):
    return dict.__getitem__(self, self.symm(key))

def __setitem__(self, key, value):
    dict.__setitem__(self, self.symm(key), value)

def __delitem__(self, key):
    return dict.__delitem__(self, self.symm(key))

def __contains__(self, key):
    return dict.__contains__(self, self.symm(key))


@staticmethod
def symm(key):
    return key if key[0] < key[1] else (key[1], key[0]).