使用哪种数据结构实现具有多个索引的字典?

13

我正在寻找一种数据结构,可以在两个不同的索引下保存相同的值,我可以通过任意一个索引访问数据。

例如:

x = mysticalDataStructure()
x.add(1,'karl', dog)
x.add(2,'lisa', cat)

$ x[1].age
2
$ x['karl'].age
2
$ x[1].age = 4
$ x['karl'].age
4

有没有预先准备好的东西,或者自己编写的最佳方法是什么(我需要通过索引(从0到n以1为增量)和字符串访问)。

collections.ordereddict似乎没有快速访问特定位置的随机访问,就我所知,只能使用迭代器遍历直到达到元素(可以按正确顺序插入)。


如果您通过一个键更改了一个值,您是否期望使用另一个键检索到新值? - martineau
@martineau:是的,正是如此。 - ted
请查看此答案/模块:https://dev59.com/_mgu5IYBdhLWcg3wRlBh#16966988 - formiaczek
3个回答

12

你不能只是使用字典吗?是否有特殊的原因:

x = {}
x[1] = x['karl'] = dog
x[2] = x['lisa'] = cat

然后你可以通过以下方式访问它。

如果您真的不想重复自己,可以这样做:

class MysticalDataStructure(dict):
    def add(self, key1, key2, value):
        return self[key1] = self[key2] = value

x = MysticalDataStructure()
x.add(1, 'karl', dog)
x.add(2, 'lisa', cat)

修改x[1]不会修改x['karl']吗? - Hans Z
2
x[1] = x['karl'] = 3,x[1] = 2 不会改变 x['karl'] 的值。 - Hans Z
+1 简单易懂,但在我的特定情况下 astynax 解决方案更好(否则我可以使用您的解决方案并包装所有不可变对象,但我的不可变对象比可变对象多,所以这不太实际)。 - ted
Python使用字典作为映射表。但在现实生活中,你可不想这么做 :) - NoBugs
聪明而简单! - Gill Bates
显示剩余4条评论

12
class MultiKeyDict(object):

    def __init__(self, **kwargs):
        self._keys = {}
        self._data = {}
        for k, v in kwargs.iteritems():
            self[k] = v

    def __getitem__(self, key):
        try:
            return self._data[key]
        except KeyError:
            return self._data[self._keys[key]]

    def __setitem__(self, key, val):
        try:
            self._data[self._keys[key]] = val
        except KeyError:
            if isinstance(key, tuple):
               if not key:
                  raise ValueError(u'Empty tuple cannot be used as a key')
               key, other_keys = key[0], key[1:]
            else:
               other_keys = []
            self._data[key] = val
            for k in other_keys:
                self._keys[k] = key

    def add_keys(self, to_key, new_keys):
        if to_key not in self._data:
            to_key = self._keys[to_key]
        for key in new_keys:
            self._keys[key] = to_key


    @classmethod
    def from_dict(cls, dic):
        result = cls()
        for key, val in dic.items():
            result[key] = val
        return result

使用方法:

>>> d = MultiKeyDict(a=1, b=2)
>>> d['c', 'd'] = 3 # two keys for one value
>>> print d['c'], d['d']
3 3
>>> d['c'] = 4
>>> print d['d']
4
>>> d.add_keys('d', ('e',))
>>> d['e']
4
>>> d2 = MultiKeyDict.from_dict({ ('a', 'b'): 1 })
>>> d2['a'] = 2
>>> d2['b']
2

1
可能还应该有一个 __delitem__() - martineau
@martineau,这只是一个简单的草图,用于解释概念。 - Aleksei astynax Pirogov
太完美了,这真是太棒了。虽然我需要进行两个列表访问,但我也可以存储不可变类型的实例。 - ted
@ted,我稍微修改了这个类——现在一个键的更改会反映到另一个键上(如果值相同)。 - Aleksei astynax Pirogov
@astynax 我已经自己完成了,我还添加了一个删除函数。顺便说一下,我通过保持一个类内部计数器来进行索引,这样只有在整数溢出时才会发生冲突,而不是偶然发生。对于删除操作,遍历键字典并删除所有映射到相同键值的条目即可(可以保留反向引用,但这仅在从大型集合频繁删除时才有用)。谢谢你的更新。我不会在这里发布我的代码,因为它不够Pythonic,我会审慎行事再看,你已经更新了代码,我最初回来是为了展示我的修改。 - ted
显示剩余11条评论

1

只需使用三张地图。

maps = [dict(), dict(), dict()]

def insert(rec):
   maps[0][rec[0]] = rec
   maps[1][rec[1]] = rec
   maps[2][rec[2]] = rec

更改rec对象的关键属性将需要重新插入。就像更改对象的键一样,与任何其他映射一样。

毕竟,这些映射只是将键映射到对象。它们实际上不存储对象的副本(它只是没有被垃圾回收)。因此,映射只是一个索引,仅此而已。如果您想要三个索引,请使用三个映射。编写一些粘合代码函数来管理它们。

正如Trevor所提到的,您还可以使用共享字典:

index = dict()

def insert(rec):
    index[rec[0]] = rec
    index[rec[1]] = rec
    index[rec[2]] = rec

然后您可以通过以下任一方式访问它。

但要注意键冲突!


+1回答了我糟糕的示例,但您忘记了例如我还有来自对象之外的索引,例如1,就像是dog的ID。 - ted

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接