双向/反向地图

时间:2009-09-21 19:27:35

标签: python

我在python中做这个交换机的事情,我需要跟踪谁和谁说话,所以如果Alice - >鲍勃,然后暗示鲍勃 - >爱丽丝。

是的,我可以填充两个哈希映射,但我想知道是否有人有想法用一个哈希映射。

或建议另一种数据结构。

没有多个对话。假设这是一个客户服务呼叫中心,所以当Alice拨入交换机时,她只会和Bob通话。他的回复也只发给她。

15 个答案:

答案 0 :(得分:79)

您可以通过继承dict并添加所需的逻辑来创建自己的字典类型。这是一个基本的例子:

class TwoWayDict(dict):
    def __setitem__(self, key, value):
        # Remove any previous connections with these values
        if key in self:
            del self[key]
        if value in self:
            del self[value]
        dict.__setitem__(self, key, value)
        dict.__setitem__(self, value, key)

    def __delitem__(self, key):
        dict.__delitem__(self, self[key])
        dict.__delitem__(self, key)

    def __len__(self):
        """Returns the number of connections"""
        return dict.__len__(self) // 2

它的工作原理如下:

>>> d = TwoWayDict()
>>> d['foo'] = 'bar'
>>> d['foo']
'bar'
>>> d['bar']
'foo'
>>> len(d)
1
>>> del d['foo']
>>> d['bar']
Traceback (most recent call last):
  File "<stdin>", line 7, in <module>
KeyError: 'bar'

我确信我没有涵盖所有情况,但这应该让你开始。

答案 1 :(得分:39)

在您的特殊情况下,您可以将它们存储在一个字典中:

relation = {}
relation['Alice'] = 'Bob'
relation['Bob'] = 'Alice'

因为你所描述的是对称关系。 A -> B => B -> A

答案 2 :(得分:21)

我只需使用

填充第二个哈希值
reverse_map = dict((reversed(item) for item in forward_map.items()))

答案 3 :(得分:13)

我知道这是一个较老的问题,但我想提一下这个问题的另一个很好的解决方案,即python包bidict。它非常直接使用:

from bidict import bidict
map = bidict(Bob = "Alice")
print(map["Bob"])
print(map.inv["Alice"])

答案 4 :(得分:8)

假设您可以节省内存,两个哈希映射实际上可能是性能最快的解决方案。我会把它们包装在一个单独的类中 - 程序员的负担是确保两个哈希映射正确同步。

答案 5 :(得分:5)

您有两个不同的问题。

  1. 您有一个“对话”对象。它指的是两个人。由于一个人可以有多个对话,因此你有多对多的关系。

  2. 您有一个从人员到对话列表的地图。转换将有一对人。

  3. 做这样的事情

    from collections import defaultdict
    switchboard= defaultdict( list )
    
    x = Conversation( "Alice", "Bob" )
    y = Conversation( "Alice", "Charlie" )
    
    for c in ( x, y ):
        switchboard[c.p1].append( c )
        switchboard[c.p2].append( c )
    

答案 6 :(得分:4)

不,如果不创建两个词典,就没有办法做到这一点。如何在仅提供一本字典的同时实现这一目标,同时继续提供可比性能呢?

最好创建一个封装两个词典并公开所需功能的自定义类型。

答案 7 :(得分:1)

您可以使用recipe 578224上的Python Cookbook中显示的DoubleDict

答案 8 :(得分:1)

另一种可能的解决方案是实现dict的子类,它保存原始字典并跟踪它的反转版本。如果键和值重叠,则保留两个单独的dicts非常有用。

class TwoWayDict(dict):
    def __init__(self, my_dict):
        dict.__init__(self, my_dict)
        self.rev_dict = {v : k for k,v in my_dict.iteritems()}

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

    def pop(self, key):
        self.rev_dict.pop(self[key])
        dict.pop(self, key)

    # The above is just an idea other methods
    # should also be overridden. 

示例:

>>> d = {'a' : 1, 'b' : 2} # suppose we need to use d and its reversed version
>>> twd = TwoWayDict(d)    # create a two-way dict
>>> twd
{'a': 1, 'b': 2}
>>> twd.rev_dict
{1: 'a', 2: 'b'}
>>> twd['a']
1
>>> twd.rev_dict[2]
'b'
>>> twd['c'] = 3    # we add to twd and reversed version also changes
>>> twd
{'a': 1, 'c': 3, 'b': 2}
>>> twd.rev_dict
{1: 'a', 2: 'b', 3: 'c'}
>>> twd.pop('a')   # we pop elements from twd and reversed  version changes
>>> twd
{'c': 3, 'b': 2}
>>> twd.rev_dict
{2: 'b', 3: 'c'}

答案 9 :(得分:1)

pypi上有集合扩展库:https://pypi.python.org/pypi/collections-extended/0.6.0

使用双射类非常简单:

RESPONSE_TYPES = bijection({
    0x03 : 'module_info',
    0x09 : 'network_status_response',
    0x10 : 'trust_center_device_update'
})
>>> RESPONSE_TYPES[0x03]
'module_info'
>>> RESPONSE_TYPES.inverse['network_status_response']
0x09

答案 10 :(得分:1)

我喜欢其中一项评论中关于二分法的建议。

pip install bidict

用法:

# This normalization method should save hugely as aDaD ~ yXyX have the same form of smallest grammar.
# To get back to your grammar's alphabet use trans

def normalize_string(s, nv=None):
    if nv is None:
        nv = ord('a')
    trans = bidict()
    r = ''
    for c in s:
        if c not in trans.inverse:
            a = chr(nv)
            nv += 1
            trans[a] = c
        else:
            a = trans.inverse[c]
        r += a
    return r, trans


def translate_string(s, trans):
    res = ''
    for c in s:
        res += trans[c]
    return res


if __name__ == "__main__":
    s = "bnhnbiodfjos"

    n, tr = normalize_string(s)
    print(n)
    print(tr)
    print(translate_string(n, tr))    

因为没有太多相关文档。但是我可以正常使用所有需要的功能。

打印:

abcbadefghei
bidict({'a': 'b', 'b': 'n', 'c': 'h', 'd': 'i', 'e': 'o', 'f': 'd', 'g': 'f', 'h': 'j', 'i': 's'})
bnhnbiodfjos

答案 11 :(得分:1)

一种较为简单的方法,仍然使用反转:

dict(map(reversed, my_dict.items()))

答案 12 :(得分:0)

kjbuckets C扩展模块提供了一个“图形”数据结构,我相信它可以为您提供所需的数据。

答案 13 :(得分:0)

通过扩展pythons dict类来实现另一个双向字典实现,以防你不喜欢其他任何一个:

class DoubleD(dict):
    """ Access and delete dictionary elements by key or value. """ 

    def __getitem__(self, key):
        if key not in self:
            inv_dict = {v:k for k,v in self.items()}
            return inv_dict[key]
        return dict.__getitem__(self, key)

    def __delitem__(self, key):
        if key not in self:
            inv_dict = {v:k for k,v in self.items()}
            dict.__delitem__(self, inv_dict[key])
        else:
            dict.__delitem__(self, key)

除了在构造中使用它作为普通的python词典:

dd = DoubleD()
dd['foo'] = 'bar'

答案 14 :(得分:0)

我喜欢做这种事情的方式是这样的:

{my_dict[key]: key for key in my_dict.keys()}