Python 对称字典,其中d[a][b]==d[b][a]
我在python中有一个算法,它为值对创建度量,其中Python 对称字典,其中d[a][b]==d[b][a],python,inheritance,dictionary,Python,Inheritance,Dictionary,我在python中有一个算法,它为值对创建度量,其中m(v1,v2)==m(v2,v1)(即它是对称的)。我的想法是编写一个字典字典,其中这些值以一种内存高效的方式存储,这样就可以用任意顺序的键轻松检索它们。我喜欢从事物中继承,理想情况下,我喜欢编写一个对称dict,其中s_d[v1][v2]总是等于s_d[v2][v1],可能是根据某种排序关系检查哪个v较大,然后切换它们,以便总是首先提到较小的元素。i、 e.当调用s_d[5][2]=4时,dict of dict将把它们转过来,以便它们实际
m(v1,v2)==m(v2,v1)
(即它是对称的)。我的想法是编写一个字典字典,其中这些值以一种内存高效的方式存储,这样就可以用任意顺序的键轻松检索它们。我喜欢从事物中继承,理想情况下,我喜欢编写一个对称dict
,其中s_d[v1][v2]
总是等于s_d[v2][v1]
,可能是根据某种排序关系检查哪个v较大,然后切换它们,以便总是首先提到较小的元素。i、 e.当调用s_d[5][2]=4
时,dict of dict将把它们转过来,以便它们实际上被存储为s_d[2][5]=4
,同样用于检索数据。
我也非常愿意使用更好的数据结构,但我更喜欢使用“is-a”关系的实现,而不是只使用dict并预处理一些函数参数的实现。如图所示使用嵌套索引将非常困难。最好使用元组作为键。这样就可以对元组进行排序,并且可以访问封装的
dict
来获取值
d[2, 5] = 4
print d[5, 2]
一个明显的替代方法是使用
(v1,v2)
元组作为单个标准dict
的键,并将(v1,v2)
和(v2,v1)
插入字典,使它们引用右侧的同一对象。您可以使用a作为dict的键:
>>> 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
作为Dave Webb的frozenset的替代方案,为什么不使用如下符号:
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)
class符号(dict):
def _u获取项目(自身,密钥):
返回指令.uuu getitem(self,如果键[0]<键[1],否则键[1],键[0]))
定义设置项(自身、键、值):
说明.uuu设置项(self,如果键[0]<键[1],否则键[1],键[0]),值)
通过快速测试,获取和设置项目的速度比使用冻结集快10%以上。无论如何,这只是另一个想法。但是,它的适应性不如frozenset,因为它实际上只设置为与长度为2的元组一起使用。据我从OP上所知,这似乎不是一个问题。这里有一个稍微不同的方法,看起来很有希望。虽然
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])]
为了改进Justin Peel的解决方案,您需要添加
\uuu delitem\uuuu
和\uu包含更多字典操作的方法。因此,为了完整性
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]))
不过,我不确定这是否涵盖了所有的基础,但对于我自己的代码来说已经足够了。我会提取函数以提高可读性(对于patvarilly答案)
class符号(dict):
def _u获取项目(自身,密钥):
返回dict.\uuu getitem\uuu(self,self.symm(键))
定义设置项(自身、键、值):
dict.uuu setitem_uuuu(self,self.symm(key),value)
def uu delitem uu(self,key):
返回dict.uuu delitem(self,self.symm(键))
def___;包含_______;(自身,密钥):
返回指令。uuu包含(self,self.symm(键))
@静力学方法
def symm(钥匙):
如果键[0]小于键[1],则返回键(键[1],键[0])。
我考虑过这一点,但是值可能是非常复杂的结构,所以元组对于第一个元素来说总是非常冗余的(因为(v1,v2)和(v1,v3)将有两个v1的副本,如果我不引入一个新的结构来存储指向所有v的指针……)我想这会让它变得很不雅观。如果另一个版本很难的话,我可能会选择那个版本。。。在将[]运算符中给定的元组保存到父dict中之前,我必须重写哪个成员函数才能对其进行排序?您必须重写\uuuu getitem\uuuuuuuu()
、\uuuuuuu setitem\uuuuuuuuu()
方法@菲利克斯·亚历杭德罗·多姆贝克:除非你强迫它,否则Python不会复制。(v1,v2)和(v1,v3)将共享相同的v1。另外,创建元组的效率也很高。。。我的意思是:对于每个元组(v1,vn)到(v1,vm),dict中存储了一个不同的元组,这意味着指向v1的指针被存储了m-n次,并且所有元组元数据(不管是什么,我不认为python 2元组只占用2个指针的原始8字节)被存储了n-m次。这通常不是一个问题,但要写得尽可能干净(对于大n-m),我的想法是在第一个dict中存储指向v1的指针一次,并且只存储一次,然后再存储指向第二个dict的指针,而第二个dict又只保存指向m-n的指针。。。我不知道这是否有意义。我当然喜欢这个主意!我还是想去掉冗余,这样就不用保存第一个值两次,或者意外地保存两次。。。
>>> 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
{}
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]).