跟踪数据结构的变化。
difftrack的Python项目详细描述
difftrack是跟踪数据结构更改的工具。 它使多个“侦听器”可以看到 要更改的dict、list或任何其他数据结构 观察和支持(这些结构称为“调度员”)。
difftrack有两个主要类:
- Dispatcher-就像一个数据结构,你可以向它写入数据,也可以发送数据 对其所有侦听器的所有更改(diff)。
- Listener-一个侦听器连接到一个调度程序并应用输入 与内部结构不同,因此每个侦听器看起来都像原始数据 应用所有这些差异后的结构。
此分区允许difftrack在 应用diff的不同阶段,它使侦听器 有特殊的缩写(例如difftrack.utils.BoundedListDiffHandler 实现“前n个”列表:列表永远不会超过某个固定大小 但当删除某些项目时,会出现以前不可见的元素)。
基本用法
在下面的示例中,我们将创建一个列表分派器(您可以 使用__setitem__,^{tt8}将其作为列表写入$ 以及insert)和两个侦听器,它们将侦听diff并保持 他们自己的内部状态。
>>>importdifftrack>>>dispatcher=difftrack.ListDispatcher()>>>listener1=difftrack.ListListener()>>>listener2=difftrack.ListListener()>>>dispatcher.add_listener(listener1)>>>dispatcher.add_listener(listener2)# create listeners and add them to dispatcher>>>dispatcher.insert(0,'AAA')# insert string 'AAA' to the first position in list>>>listener1.get_snapshot()# Diffs are not applied until get_new_diffs() is called[]>>>listener1.get_new_diffs()# now we get all diffs that have not been processed yet[(difftrack.ListDiff.INSERT,0,'AAA')]>>>listener1.get_snapshot()# and we see that listener1's snapshot now contains what we expect['AAA']>>>listener2.get_snapshot()# second listener still hasn't got anything because we haven't read its diffs[]>>>dispatcher.insert(0,'BBB')# insert new string to 'BBB'>>>listener1.get_new_diffs()# we need to read new diffs to get current state[(difftrack.ListDiff.INSERT,0,'BBB')]>>>listener1.get_snapshot()# we inserted 'BBB' to first position so 'AAA' was moved to second position['BBB','AAA']>>>deldispatcher[0]# remove the first element from th list (now 'BBB')>>>listener1.get_new_diffs()[(difftrack.ListDiff.DELETE,0,None)]>>>listener1.get_snapshot()# we deleted 'BBB' so only 'AAA' remains['AAA']>>>dispatcher[0]='CCC'# overwrite the first element>>>listener1.get_new_diffs()[(difftrack.ListDiff.REPLACE,0,'CCC')]>>>listener1.get_snapshot()['CCC']# the first and only element in list was overwritten>>>listener2.get_new_diffs()# finally get all diffs for listener2[(<ListDiff.INSERT:0>,0,'AAA'),(<ListDiff.INSERT:0>,0,'BBB'),(<ListDiff.DELETE:2>,0,None),(<ListDiff.REPLACE:1>,0,'CCC')]>>>listener2.get_snapshot()# listener2 is now also up to date['CCC']
类似地,可以将difftrack与DictDispatcher和 DictListener:将更改写入 DictDispatcher在对侦听器应用diff之后,可以得到 当前字典状态的快照。
回调
on_change
我们还可以向侦听器添加回调,以便在 来了:
importdifftrack>>>dispatcher=difftrack.ListDispatcher()>>>defdouble_inserted_items(dtype,index,value):''' This generates a new diff *while the current one is processed!* '''ifdtypeisdifftrack.ListDiff.INSERT:dispatcher[index]=value*2>>>listener=difftrack.ListListener(on_change=double_inserted_items)# set function as a callback>>>dispatcher.add_listener(listener)>>>dispatcher.insert(0,7)# insert 7 at index 0 and expect that the result will be doubled>>>listener.get_new_diffs()[(difftrack.ListDiff.INSERT,0,7),(difftrack.ListDiff.REPLACE,0,14)]>>>listener.get_snapshot()[14]
在这个例子中,我们展示了on_change回调及其 和调度员一起工作。注意,我们首先使用 ListDiff.INSERT操作,但回调会触发 ListDiff.REPLACE操作。如果它再次导致ListDiff.INSERT,我们 以递归结束,10次迭代后difftrack将放弃并 提出一个例外。
on_finalize_batch
调度器可以向它的侦听器通信某个序列 差分属于一起,即形成一个batch。我们通过使用 Dispatcher作为上下文管理器,包装属于一起的diff操作。
侦听器可以提供另一个名为on_finalize_batch 每次调度程序完成批处理调度时调用。 (上下文已退出)。
>>>importdifftrack>>>dispatcher=difftrack.DictDispatcher()>>>deffinalize():print('FINALIZED')>>>defon_change(*args):print('CHANGE')>>>listener=difftrack.DictListener(on_change=on_change,on_finalize_batch=finalize)>>>dispatcher.add_listener(listener)>>>withdispatcher:# use the dispatcher as a context managerdispatcher[0]=0dispatcher[1]=1dispatcher[2]=2CHANGECHANGECHANGEFINALIZED
我们可以看到每次都调用on_change回调,但是 {TT20}$仅当我们退出上下文时。
实用程序
您可能会发现有几个实用程序很有用。
data_mapper
数据映射器对每个数据字段应用一个函数:
>>>importdifftrack>>>defmapper(data:str)->str:returndata.lower()>>>dispatcher=difftrack.ListDispatcher()>>>listener=difftrack.ListListener()>>>dispatcher.add_listener(difftrack.data_mapper(mapper)(listener))>>>dispatcher.insert(0,'AAA')>>>dispatcher.insert(0,'BBB')>>>listener.get_new_diffs()[(difftrack.ListDiff.INSERT,0,'aaa'),(difftrack.ListDiff.INSERT,0,'bbb')]>>>listener.get_snapshot()['bbb','aaa']
compact_dict_diffs
当你多次更新一个dict项,甚至删除它时 有时不想保留所有的变化。您可以使用压缩 删除相互取消或覆盖的更改:
>>>diffs=[(difftrack.DictDiff.SET,'x',123),(difftrack.DictDiff.SET,'y',456),(difftrack.DictDiff.SET,'y',9999),(difftrack.DictDiff.DELETE,'x',None),]>>>difftrack.compact_dict_diffs(diffs)[(difftrack.DictDiff.SET,'y',9999),(difftrack.DictDiff.DELETE,'x',None),]
compact_list_diffs
同样的压缩也适用于列表:
>>>diffs=[(difftrack.ListDiff.INSERT,0,'aaa'),(difftrack.ListDiff.INSERT,1,'bbb'),(difftrack.ListDiff.DELETE,0,None)(difftrack.ListDiff.REPLACE,1,'ccc'),]>>>difftrack.compact_list_diffs(diffs)[(difftrack.ListDiff.INSERT,1,'ccc'),]
BoundedListDiffHandler
如果我们想保持列表有界(限制到一定的大小),我们可以使用 difftrack.BoundedListDiffHandler。
>>>importdifftrack>>>listener=difftrack.ListListener()>>>dispatcher=difftrack.ListDispatcher()>>>dispatcher.add_listener(difftrack.BoundedListDiffHandler(listener,2))# bound listener to 2 elements>>>dispatcher.insert(0,'a')>>>dispatcher.insert(1,'b')>>>dispatcher.insert(2,'c')>>>dispatcher.insert(3,'d')>>>listener.get_new_diffs()[(difftrack.ListDiff.INSERT,0,'a'),(difftrack.ListDiff.INSERT,1,'b'),]>>>listener.get_snapshot()['a','b']>>>deldispatcher[0]>>>listener.get_new_diffs()# 'a' is deleted and 'c' moves to the empty index 1[(<ListDiff.DELETE:2>,0,None),(<ListDiff.INSERT:0>,1,'c')]>>>listener.get_snapshot()['b','c']
squash_list_diffs
此函数组列出影响连续索引的差异。
>>>importdifftrack>>>diffs=[(difftrack.ListDiff.INSERT,1,'A'),(difftrack.ListDiff.INSERT,2,'B'),(difftrack.ListDiff.INSERT,3,'C'),(difftrack.ListDiff.REPLACE,1,'D'),(difftrack.ListDiff.DELETE,1,[])]>>>list(difftrack.squash_list_diffs(diffs))[SquashResults(operation=<difftrack.ListDiff.INSERT:0>,start=1,stop=1,payload=['A','B','C']),SquashResults(operation=<difftrack.ListDiff.REPLACE:1>,start=1,stop=2,payload=['D']),SquashResults(operation=<difftrack.ListDiff.DELETE:2>,start=1,stop=2,payload=[])]
您可以看到三个连续的插入被压缩成一条消息。注意结果 不再是difftrack diff。