我希望在python中创建一个带有'roll-back'功能的字典。字典将以修订号0开头,修订版只能通过显式方法调用来提升。我不需要删除密钥,只需添加和更新密钥,值对,然后回滚。我永远不需要'前滚',也就是说,当回滚字典时,所有较新的修订都可以被丢弃,我可以重新开始重新开始。因此我想要这样的行为:
>>> rr = rev_dictionary()
>>> rr.rev
0
>>> rr["a"] = 17
>>> rr[('b',23)] = 'foo'
>>> rr["a"]
17
>>> rr.rev
0
>>> rr.roll_rev()
>>> rr.rev
1
>>> rr["a"]
17
>>> rr["a"] = 0
>>> rr["a"]
0
>>> rr[('b',23)]
'foo'
>>> rr.roll_to(0)
>>> rr.rev
0
>>> rr["a"]
17
>>> rr.roll_to(1)
Exception ...
为了清楚起见,与修订版关联的状态是在roll_rev()
方法调用之前的字典状态。因此,如果我可以在修订版中多次修改与密钥相关联的值,并且只记住最后一个修改的值。
我想要一个相当内存有效的实现:内存使用量应该与增量成正比。因此只是拥有字典的副本列表将无法扩展我的问题。人们应该假设密钥数以万计,修订数量达到数十万。
我们可以假设值是不可变的,但不必是数字。对于值为例如的情况整数,有一个相当简单的实现(有一个从修订到修订的数字增量的字典列表)。我不知道如何把它变成一般形式。也许引导整数版本并添加一个值数组?
所有人都很感激。
答案 0 :(得分:2)
只有一个字典,从键映射到(revision_number,actual_value)元组列表。当前值为the_dict[akey][-1][1]
。回滚仅涉及在每个列表的末尾弹出相应的条目。
更新:回滚示例
key1 - > [(10,'v1-10'),(20,'v1-20')]
场景1:当前版本为30,回滚到25:没有任何反应
场景2:当前30,回到15:弹出最后一个条目
场景3:当前30,回到5:弹出两个条目
更新2:更快的回滚(带权衡)
我认为你对弹出每个列表的关注更好地表达为“需要检查每个列表以查看它是否需要弹出”。使用更漂亮的数据结构(更多内存,更多时间来维护添加和更新操作中的花哨位),您可以减少回滚的时间。
添加一个数组(按修订号编号索引),其值是该修订中更改的字典值列表。
# Original rollback code:
for rlist in the_dict.itervalues():
if not rlist: continue
while rlist[-1][0] > target_revno:
rlist.pop()
# New rollback code
for revno in xrange(current_revno, target_revno, -1):
for rlist in delta_index[revno]:
assert rlist[-1][0] == revno
del rlist[-1] # faster than rlist.pop()
del delta_index[target_revno+1:]
更新3:更高级代码的完整代码
import collections
class RevDict(collections.MutableMapping):
def __init__(self):
self.current_revno = 0
self.dict = {}
self.delta_index = [[]]
def __setitem__(self, key, value):
if key in self.dict:
rlist = self.dict[key]
last_revno = rlist[-1][0]
rtup = (self.current_revno, value)
if last_revno == self.current_revno:
rlist[-1] = rtup
# delta_index already has an entry for this rlist
else:
rlist.append(rtup)
self.delta_index[self.current_revno].append(rlist)
else:
rlist = [(self.current_revno, value)]
self.dict[key] = rlist
self.delta_index[self.current_revno].append(rlist)
def __getitem__(self, key):
if not key in self.dict:
raise KeyError(key)
return self.dict[key][-1][1]
def new_revision(self):
self.current_revno += 1
self.delta_index.append([])
def roll_back(self, target_revno):
assert 0 <= target_revno < self.current_revno
for revno in xrange(self.current_revno, target_revno, -1):
for rlist in self.delta_index[revno]:
assert rlist[-1][0] == revno
del rlist[-1]
del self.delta_index[target_revno+1:]
self.current_revno = target_revno
def __delitem__(self, key):
raise TypeError("RevDict doesn't do del")
def keys(self):
return self.dict.keys()
def __contains__(self, key):
return key in self.dict
def iteritems(self):
for key, rlist in self.dict.iteritems():
yield key, rlist[-1][1]
def __len__(self):
return len(self.dict)
def __iter__(self):
return self.dict.iterkeys()
答案 1 :(得分:2)