您不会找到执行此操作的基本数据结构;您正在寻找的目标排除了所有这些目标。您可能会发现一个更深奥的方法可以做到这一点,但最简单的方法是使用复合数据结构,并行维护两个数据结构。
事实上,这就是collections.OrderedDict 在幕后所做的。但是,这不是您想要的:由于它不是为支持索引而设计的,因此它在底层使用链表来保留顺序。链表不能进行索引——缺少缓慢的线性扫描,您通常希望避免这种情况,因为如果在循环中使用它往往会导致 O(n^2) 对您不利。
这是一个简单的实现。它维护两个数据结构:一个列表,在设置时保留项目的顺序,以及一个字典,用于按键快速查找。两者都保存值,并且都保存对方的键:字典保存列表中的索引,列表保存字典中的键。这使得相互引用每个数据结构变得容易,因此它可以有效地处理赋值和迭代。
请注意,这并没有实现所有操作,仅实现基本操作:dict-style assignment a['x'] = 1、dict-style lookup a['x']、list-style assignment a.set_value_by_index(0, 1) 和 list-style lookup a.get_value_by_index(0)。
另请注意:这不会对 dict 样式和 list 样式的操作使用相同的语法。这是令人困惑和邪恶的,迟早会严重咬你。这不会将a[0] 变成列表样式的查找;如果这是您想要的,请明确并使用get_value_by_index。不要魔法,试着根据参数类型来猜测。
最后,它提供了简单的 dict 风格的迭代,像 dict 一样产生键。实现 iteritems 和 itervalues 或 Python3 视图之类的东西是明显的扩展。
class IndexableUniqueList(object):
"""
>>> a = IndexableUniqueList()
>>> a['x'] = 1
>>> a['x']
1
>>> a['y'] = 2
>>> a['y']
2
>>> a.get_key_by_index(0)
'x'
>>> a.get_value_by_index(0)
1
>>> a.get_key_by_index(1)
'y'
>>> a.get_value_by_index(1)
2
>>> a['x'] = 3
>>> a.get_key_by_index(0)
'x'
>>> a.get_value_by_index(0)
3
>>> a.set_value_by_index(0, 4)
>>> a['x']
4
>>> [val for val in a]
['x', 'y']
"""
def __init__(self):
self.items_by_index = []
self.items_by_key = {}
def __getitem__(self, key):
return self.items_by_key[key][1]
def __setitem__(self, key, value):
if key in self.items_by_key:
idx, old_value = self.items_by_key[key]
self.items_by_key[key] = (idx, value)
self.items_by_index[idx] = (key, value)
return
idx = len(self.items_by_index)
self.items_by_key[key] = (idx, value)
self.items_by_index.append((key, value))
def get_key_by_index(self, idx):
return self.items_by_index[idx][0]
def get_value_by_index(self, idx):
key = self.get_key_by_index(idx)
return self.items_by_key[key][1]
def set_value_by_index(self, idx, value):
key = self.items_by_index[idx][0]
self[key] = value
def __iter__(self):
for key, value in self.items_by_index:
yield key