【问题标题】:How to detect what element of a nested list has changed? (python)如何检测嵌套列表的哪个元素发生了变化? (Python)
【发布时间】:2014-12-23 20:03:24
【问题描述】:

我有一个大的 2D(列表列表)列表,每个元素都包含一个整数、字符串和字典的列表。我希望能够确定任何被修改的元素的“路径”(例如 [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

【问题讨论】:

  • 也许您可以签入__setattr__ 值的类型,如果它是一个列表,您可以将其包装在 Notify 对象中?
  • 另外,您可以向 Notify 类添加一些功能,例如父类,因此如果值更改并且 Notify 类不是根,它可以向上传递事件
  • 或许this的回答可能对你有所帮助。
  • @lelloman 我对 python 中的类很陌生,但是新对象的新通知类仍然会像那些对象一样(仍然有 .append() 用于列表等)吗?
  • @Mauro Baraldi 谢谢,这看起来像我需要的 - 我怎么没找到!?

标签: python list class


【解决方案1】:

我的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]

【讨论】:

    【解决方案2】:

    您需要在可跟踪列表的(可跟踪的)列表中创建一个报告链,其中每个列表都向其父级报告修改。在您的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']
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-02
      • 2012-04-07
      • 2017-04-02
      • 2011-08-27
      • 2016-08-18
      • 1970-01-01
      相关资源
      最近更新 更多