时间:2010-07-23 13:38:21

标签: python hashtable bidirectional

7 个答案:

答案 0 :(得分:43)

这是一个双向dict的类,受Finding key from value in Python dictionary启发并修改为允许以下2)和3)。

请注意:

  • 1)当标准字典bd.inverse被修改时,逆目录 bd会自动更新。
  • 2)逆目录 bd.inverse[value]始终是key列表bd[key] == value
  • 3)与https://pypi.python.org/pypi/bidict中的bidict模块不同,此处我们可以有2个具有相同值的键,这是非常重要的

<强>代码:

class bidict(dict):
    def __init__(self, *args, **kwargs):
        super(bidict, self).__init__(*args, **kwargs)
        self.inverse = {}
        for key, value in self.items():
            self.inverse.setdefault(value,[]).append(key) 

    def __setitem__(self, key, value):
        if key in self:
            self.inverse[self[key]].remove(key) 
        super(bidict, self).__setitem__(key, value)
        self.inverse.setdefault(value,[]).append(key)        

    def __delitem__(self, key):
        self.inverse.setdefault(self[key],[]).remove(key)
        if self[key] in self.inverse and not self.inverse[self[key]]: 
            del self.inverse[self[key]]
        super(bidict, self).__delitem__(key)

用法示例:

bd = bidict({'a': 1, 'b': 2})  
print(bd)                     # {'a': 1, 'b': 2}                 
print(bd.inverse)             # {1: ['a'], 2: ['b']}
bd['c'] = 1                   # Now two keys have the same value (= 1)
print(bd)                     # {'a': 1, 'c': 1, 'b': 2}
print(bd.inverse)             # {1: ['a', 'c'], 2: ['b']}
del bd['c']
print(bd)                     # {'a': 1, 'b': 2}
print(bd.inverse)             # {1: ['a'], 2: ['b']}
del bd['a']
print(bd)                     # {'b': 2}
print(bd.inverse)             # {2: ['b']}
bd['b'] = 3
print(bd)                     # {'b': 3}
print(bd.inverse)             # {2: [], 3: ['b']}

答案 1 :(得分:33)

答案 2 :(得分:31)

答案 3 :(得分:2)

下面的代码片段实现了一个可逆(双射)地图:

class BijectionError(Exception):
    """Must set a unique value in a BijectiveMap."""

    def __init__(self, value):
        self.value = value
        msg = 'The value "{}" is already in the mapping.'
        super().__init__(msg.format(value))


class BijectiveMap(dict):
    """Invertible map."""

    def __init__(self, inverse=None):
        if inverse is None:
            inverse = self.__class__(inverse=self)
        self.inverse = inverse

    def __setitem__(self, key, value):
        if value in self.inverse:
            raise BijectionError(value)

        self.inverse._set_item(value, key)
        self._set_item(key, value)

    def __delitem__(self, key):
        self.inverse._del_item(self[key])
        self._del_item(key)

    def _del_item(self, key):
        super().__delitem__(key)

    def _set_item(self, key, value):
        super().__setitem__(key, value)

此实现的优势在于inverse的{​​{1}}属性又是BijectiveMap。因此,您可以执行以下操作:

BijectiveMap

答案 4 :(得分:1)

答案 5 :(得分:1)

不幸的是,评分最高的答案bidict无效。

共有三个选项:

  1. 子类字典:您可以创建dict的子类,但请注意。您需要编写updatepopinitializersetdefault的自定义实现。 dict实现不调用__setitem__。这就是评分最高的答案存在问题的原因。

  2. 从UserDict继承:这就像一个字典,不同之处在于所有例程都可以正确调用。它在幕后data中使用字典。您可以阅读Python Documentationuse a simple implementation of a by directional list that works in Python 3。抱歉,没有逐字记录:我不确定其版权。

  3. 从抽象基类继承:从collections.abc继承将帮助您获得新类的所有正确协议和实现。除非它也可以加密并缓存到数据库,否则这对于双向字典来说是多余的。

TL; DR-使用this作为代码。阅读Trey Hunnerarticle了解详情。

答案 6 :(得分:0)

首先,您必须确保值映射的键是一对一的,否则,无法构建双向映射。

第二,数据集有多大?如果没有太多数据,只需使用2个单独的地图,并在更新时更新它们。或者更好的是,使用现有的解决方案,如Bidict,它只是2个dicts的包装,内置更新/删除。

但是如果数据集很大,并且不希望保持2个dicts:

  • 如果键和值都是数字,请考虑使用的可能性 插值以近似映射。如果是绝大多数的 映射函数(及其中)可以覆盖键值对 反向功能),那么你只需要在地图中记录异常值。

  • 如果大部分访问都是单向的(key-&gt; value),那么它就是完全的 好的是逐步建立反向地图,交换时间为
    空间。

代码:

d = {1: "one", 2: "two" }
reverse = {}

def get_key_by_value(v):
    if v not in reverse:
        for _k, _v in d.items():
           if _v == v:
               reverse[_v] = _k
               break
    return reverse[v]