leo
leo

Reputation: 8520

Run code when an element is added to a list

How do I detect when something is added to a list? Is there an equivalent to the dictionary __setitem__ method, that will get called when something is added to the list by insert, extend, append or using + or += (__add__, __iadd__), or some other method that I probably forgot about? Or do I need to hook into each of those methods, one by one?

Upvotes: 4

Views: 264

Answers (2)

obskyr
obskyr

Reputation: 1459

You'll need to override each method separately. Especially as the operations you've mentioned are different in nature - append, insert, extend, and += modify the list in place while + creates a new list.

If you're feeling fancy, this is a potential way to do it without having to write too much boilerplate:

class MyList(list):
    pass

for method in ['append', 'insert', 'extend', '__add__', '__iadd__']:
    def code_added(self, *args, **kwargs):
        # Your code here
        getattr(super(MyList, self), method)(*args, **kwargs)

    setattr(MyList, method, code_added)

Depending on what the code you want to run accesses, you might need to handle __add__ separately.

Upvotes: 4

Jean-François Fabre
Jean-François Fabre

Reputation: 140188

as obskyr answer is suggesting, you have to define a child class of list and override a lot of methods, and test carefully to see if you're not missing something.

My approach uses a deeper hook using __getattribute__ (for method calls), __iadd__ (for +=) and __setitem__ (for slice assignment) to catch the maximum of changes, and call the original parent method so it acts like a generic middleman:

class MyList(list):
    def __getattribute__(self,a):
        if a in {"append","extend","remove","insert","pop","reverse","sort","clear"}:
            print("modification by {}".format(a))
        else:
            print("not modified {}".format(a))
        return list.__getattribute__(self,a)

    def __iadd__(self,v):
        print("in place add")
        return list.__iadd__(self,v)

    def __setitem__(self,i,v):
        print("setitem {},{}".format(i,v))
        return list.__setitem__(self,i,v)

l = MyList()

l.append(12)
l.extend([12])
l.remove(12)
print(l)
l[:] = [4,5,6]
l += [5]
print(l)

output:

modification by append
modification by extend
modification by remove
[12]
setitem slice(None, None, None),[4, 5, 6]
in place add
[4, 5, 6, 5]

as you see

  1. the list is modified properly
  2. all changes are detected

I may have missed some accesses, but that seems pretty close to me.

Upvotes: 4

Related Questions