python中1:1映射的数据结构?

python中1:1映射的数据结构?,python,data-structures,Python,Data Structures,我有一个问题,需要键到值的可逆1:1映射 这意味着有时我想找到给定键的值,但有时我想找到给定值的键。键和值都保证唯一 x = D[y] y == D.inverse[x] 显而易见的解决方案是每次我想要反向查找时简单地反转字典:反转字典非常容易 另一种选择是创建一个新类,该类将两个字典联合起来,每种查找一个字典。这很可能很快,但占用的内存是单个dict的两倍 那么,有没有更好的结构我可以使用 我的应用程序要求这应该是非常快的,并使用尽可能少的内存 结构必须是可变的,并且强烈希望改变对象不应导

我有一个问题,需要键到值的可逆1:1映射

这意味着有时我想找到给定键的值,但有时我想找到给定值的键。键和值都保证唯一

x = D[y]
y == D.inverse[x]
显而易见的解决方案是每次我想要反向查找时简单地反转字典:反转字典非常容易

另一种选择是创建一个新类,该类将两个字典联合起来,每种查找一个字典。这很可能很快,但占用的内存是单个dict的两倍

那么,有没有更好的结构我可以使用

  • 我的应用程序要求这应该是非常快的,并使用尽可能少的内存
  • 结构必须是可变的,并且强烈希望改变对象不应导致其变慢(例如,强制完全重新索引)
  • 我们可以保证键或值(或两者)都是整数
  • 很可能需要该结构来存储数千个或数百万个项目
  • 键和值保证是唯一的,即len(set(x))==len(x)表示[D.键(),D.值()]
另一种选择是创建一个新类,该类将两个字典联合起来,每种查找一个字典。这很可能会消耗两倍于单个dict的内存

不太可能,因为它们只会持有对同一数据的两个引用。在我看来,这是一个不错的解决方案


您考虑过内存中的数据库查找吗?我不确定它在速度上会如何比较,但在关系数据库中的查找速度可能非常快。

假设您有一个键,可以用来查找更复杂的可变对象,只需将该键作为该对象的属性即可。看起来您最好还是稍微考虑一下数据模型

另一个选择是制作一个新的 类,该类将两个词典结合在一起, 每种查找一个。那个 很可能会很快,但会 占用的内存是内存的两倍 单条命令

不是真的。你量过了吗?由于两个字典都将对相同对象的引用用作键和值,因此所花费的内存将只是字典结构。这比的两倍要少得多,而且无论数据大小如何,这都是一个固定的数量

我的意思是,实际数据不会被复制。所以你会花很少的额外记忆

例如:

a = "some really really big text spending a lot of memory"

number_to_text = {1: a}
text_to_number = {a: 1}
“真的很大”字符串只存在一个副本,因此您最终只需要多花一点内存。这通常是可以负担得起的

我无法想象一个解决方案,在按值查找时,如果您没有花费足够的内存来存储反向查找哈希表(这正是“United two
dict
s”解决方案中正在做的事情),那么您将具有键查找速度。

“我们可以保证键或值(或两者)都是整数。”

写得很奇怪--“键或值(或两者兼有)”感觉不对。要么都是整数,要么不都是整数

听起来它们都是整数

或者,听起来好像您正在考虑用一个整数值替换目标对象,这样您只有一个被整数引用的副本。这是一种虚假的经济。只需保留目标对象。实际上,所有Python对象都是引用。实际复制的工作很少

让我们假设您只有两个整数,并且可以对其中一个进行查找。一种方法是使用堆队列或对分模块来维护整型键值元组的有序列表

您有一个heapq
(键、值)
元组。或者,如果基础对象更复杂,则使用
(key,object
)元组

您还有另一个heapq
(值,键)
元组。或者,如果您的底层对象更复杂,
(otherkey,object)
元组

“插入”变为两个插入,一个插入到heapq结构化列表中


密钥查找在一个队列中;值查找在另一个队列中。使用
对分(列表,项目)
进行查找

碰巧我发现自己一直在问这个问题(特别是昨天)。我同意制作两本词典的方法。做一些基准测试,看看它占用了多少内存。我从不需要使它可变,但如果它有用的话,我是这样抽象它的:

class BiDict(list):
    def __init__(self,*pairs):
        super(list,self).__init__(pairs)
        self._first_access = {}
        self._second_access = {}
        for pair in pairs:
            self._first_access[pair[0]] = pair[1]
            self._second_access[pair[1]] = pair[0]
            self.append(pair)

    def _get_by_first(self,key):
        return self._first_access[key]

    def _get_by_second(self,key):
        return self._second_access[key]

    # You'll have to do some overrides to make it mutable
    # Methods such as append, __add__, __del__, __iadd__
    # to name a few will have to maintain ._*_access

class Constants(BiDict):
    # An implementation expecting an integer and a string
    get_by_name = BiDict._get_by_second
    get_by_number = BiDict._get_by_first

t = Constants(
        ( 1, 'foo'),
        ( 5, 'bar'),
        ( 8, 'baz'),
    )

>>> print t.get_by_number(5)
bar
>>> print t.get_by_name('baz')
8
>>> print t
[(1, 'foo'), (5, 'bar'), (8, 'baz')]

使用sqlite怎么样?只需创建一个带有两列表的:memory:数据库。您甚至可以添加索引,然后按任意一个进行查询。如果你要经常使用它,就把它包装在一个类中

以下是我自己解决这个问题的方法:

class TwoWay:
    def __init__(self):
       self.d = {}
    def add(self, k, v):
       self.d[k] = v
       self.d[v] = k
    def remove(self, k):
       self.d.pop(self.d.pop(k))
    def get(self, k):
       return self.d[k]
目标是使其对用户尽可能透明。唯一引入的重要属性是
partner

onetoonotice
dict中的子类-我知道这一点,但我想我已经涵盖了常见用例。后端非常简单,它(
dict1
)与“合作伙伴”
onetonotice
dict2
)保持着一个弱点,这是它的反面。修改
dict1
时,
dict2
也相应更新,反之亦然

从文档字符串:

>>> dict1 = OneToOneDict()
>>> dict2 = OneToOneDict()
>>> dict1.partner = dict2
>>> assert(dict1 is dict2.partner)
>>> assert(dict2 is dict1.partner)
>>> dict1['one'] = '1'
>>> dict2['2'] = '1'
>>> dict1['one'] = 'wow'
>>> assert(dict1 == dict((v,k) for k,v in dict2.items()))
>>> dict1['one'] = '1'
>>> assert(dict1 == dict((v,k) for k,v in dict2.items()))
>>> dict1.update({'three': '3', 'four': '4'})
>>> assert(dict1 == dict((v,k) for k,v in dict2.items()))
>>> dict3 = OneToOneDict({'4':'four'})
>>> assert(dict3.partner is None)
>>> assert(dict3 == {'4':'four'})
>>> dict1.partner = dict3
>>> assert(dict1.partner is not dict2)
>>> assert(dict2.partner is None)
>>> assert(dict1.partner is dict3)
>>> assert(dict3.partner is dict1)
>>> dict1.setdefault('five', '5')
>>> dict1['five']
'5'
>>> dict1.setdefault('five', '0')
>>> dict1['five']
'5'

当我有空闲时间时,我打算制作一个不存储东西两次的版本。不知道什么时候会这样:)

这本字典有多大?你确定两个副本不能放入内存吗?2-dicts类是迄今为止最好的!在这种情况下,我不能-一边的对象是numpy.int64s-应用程序的目的是将一个非常简单的数字图论类调整为看起来更自然的python类。在这种情况下,一个flyweight就可以了。这是一个相当明确的声明:每个键/值对中至少有一个项是整数,有时两个项都是整数