我有一个大的2D(列表列表)列表,每个元素都包含一个int,string和dicts列表。我希望能够确定任何修改过的元素的'路径'(例如[2] [3] [2] [“items”] [2]!) 。这个列表很大,可以浏览并查看更改内容!理想情况下,我也想要一个新元素的副本,虽然这可以在以后找到。
我的第一次尝试是创建一个类,并覆盖其__setattr__
方法:
class Notify():
def __setattr__(self, name, value):
self.__dict__[name] = value #Actually assign the value
print name, value #This will tell us when it fires
但是,__setattr__
方法仅在设置未被索引(或键)访问的变量时触发,因为这似乎将调用外包给包含的list()/ dict()类而不是我们的类。
>>> test = Notify()
>>> test.var = 1 #This is detected
var 1
>>> test.var = [1,2,3] #Now let's set it to a list
var [1, 2, 3]
>>> test.var[0] = 12 #But when we assign a value via an index, it doesn't fire
>>> test.var
[12, 2, 3] #However it still assigns the value, so it must be talking to the list itself!
因此,总而言之,我想(真正的任何方法)告诉我发生了什么(索引/键列表),这需要在发生时发生,因为扫描整个过程太昂贵名单。我也不能依赖修改列表的代码来提供细节。如果这对第n个嵌套列表是不可能的,我可以使用仅给出前两个索引的东西,因为数据不会太大而无法扫描。提前感谢您的帮助!
编辑:虽然这个问题Track changes to lists and dictionaries in python?似乎接近我的需要,但仍然没有快乐。不幸的是,我对课程不是很好,需要有人的帮助! 编辑:看了这个Python: Right way to extend list让我觉得继承list
可能是一个坏主意。我使用代理类代替了以下代码。然而,最初的问题仍然存在,对嵌套列表的修改不会记录。课堂作文,而不是继承是一个好主意吗?
from UserList import UserList
class NotifyList(UserList):
def __init__(self, initlist=None):
self.data = []
if initlist is not None:
if type(initlist) is list:
self.data[:] = initlist
elif isinstance(initlist, NotifyList):
self.data[:] = initlist.data[:]
else:
self.data = list(initlist)
def __setitem__(self, key, item):
if type(item) is list:
self.data[key] = NotifyList(item)
else:
self.data[key] = item
print key, item
def append(self, item):
if type(item) is list:
self.data.append(NotifyList(item))
else:
self.data.append(item)
print self.index(item), item
答案 0 :(得分:1)
您需要在(可跟踪的)可跟踪列表列表中创建报告链,其中每个列表都会报告对其父级的修改。在您的NotifyList
类中,向构造函数添加父项的参数,以及父项将知道新项的ID的参数 - 当父项是列表时,这将是列表索引:
class NotifyList(UserList):
def __init__(self, inilist=None, parent=None, id=None):
self.parent = parent
self.id = id
# remainder of __init__()...
修改发生时,应通知家长。例如,在__setitem__
:
def __setitem__(self, key, item):
if type(item) is list:
self.data[key] = NotifyList(item, self, str(key)) # Don't forget the new item's parent
else:
self.data[key] = item
self.alertParent(str(key), str(item)) # Report up the chain instead of printing
alertParent()
是:
def alertParent(self, key, item):
strChange = "[{0}] = {1}".format(key, item)
self.parent.notifyChange(self.id, strChange)
notifyChange()
如何运作?
def notifyChange(self, childKey, strChangeInChild):
strChange = "[{0}]{1}".format(childKey, strChangeInChild)
self.parent.notifyChange(self.id, strChange)
它只是将通知传播到链中,将自己的ID添加到消息中。
唯一缺失的链接是,报告链顶端会发生什么?最后应打印更改消息。通过重用alertParent()
:
def alertParent(self, key, item):
if self.parent is None: # I am the root
print "[{0}]{1}".format(key, item)
else:
# remainder of alertParent() shown above...
...
def notifyChange(self, childKey, strChangeInChild):
if self.parent is None: # I am the root
self.alertParent(childKey, strChangeInChild) # Actually just prints a change msg
else:
# remainder of notifyChange() shown above...
我编写了这个代码,完整版本可用here [Google Doc](关于我上面提到的内容,有一些简单的错误修复)。在行动:
>>> from test import NotifyList
>>> top = NotifyList([0]*3, None, None) # Now we have [0 0 0]
>>> print top
NList-[0, 0, 0]
>>> top[0] = NotifyList([0]*3, top, 0) # Now we have [ [0 0 0] 0 0 ]
[0] = NList-[0, 0, 0] #-------------- The tracking msg is fired
>>> print top
NList-[<test.NotifyList object at 0x0000000002163320>, 0, 0]
>>> top[0][1] = NotifyList([0]*3, top[0], 1) # Now we have [ [[0 0 0] 0 0] 0 0 ]
[0][1] = NList-[0, 0, 0] #----------- The tracking msg fired again
>>> top[0][1][2] = "this is a string" # Now we have [ [[0 0 "this is a string] 0 0] 0 0 ]
[0][1][2] = this is a string #------- And another tracking msg
>>> print top[0][1][2]
this is a string
>>> print top[0][1]
NList-[0, 0, 'this is a string']
答案 1 :(得分:0)
我的jsonfile模块检测到(嵌套的)JSON兼容Python对象的更改。只需将JSONFileRoot
子类化即可使更改检测适应您的需求。
>>> import jsonfile
>>> class Notify(jsonfile.JSONFileRoot):
... def on_change(self):
... print(f'notify: {self.data}')
...
>>> test = Notify()
>>> test.data = 1
notify: 1
>>> test.data = [1,2,3]
notify: [1, 2, 3]
>>> test.data[0] = 12
notify: [12, 2, 3]
>>> test.data[1] = {"a":"b"}
notify: [12, {'a': 'b'}, 3]
>>> test.data[1]["a"] = 20
notify: [12, {'a': 20}, 3]