Is there some general way to get a class to run a function when any of its attributes are modified? I wondered if some subprocess could be running to monitor changes to the class, but maybe there's a way to inherit from class
and modify some on_change
function that is a part of the Python class, a bit like how the default __repr__
method of a class can be modified. What would be some sensible approach here?
The actual application is not to just do a printout but to update entries in a database that correspond to data attributes of instantiated classes.
#!/usr/bin/env python
class Event(object):
def __init__(self):
self.a = [10, 20, 30]
self.b = 15
#def _on_attribute_change(self):
# print(f'attribute \'{name_of_last_attribute_that_was_changed}\' changed')
event = Event()
event.a[1] = 25
# printout should happen here: attribute 'a' changed
event.a.append(35)
# printout should happen here: attribute 'a' changed
event.c = 'test'
# printout should happen here: attribute 'c' changed
You can override the __setattr__
magic method.
class Foo:
def on_change(self):
print("changed")
def __setattr__(self, name, value):
self.__dict__[name] = value
self.on_change()
You can override __setattr__
.
class Event:
def __init__(self):
self.a = [10, 20, 30]
self.b = 15
def __setattr__(self, attr, value):
print(f'attribute {attr} changed')
super().__setattr__(attr, value)
However, this only detects assignment directly to the attribute. event.a[1] = 25
is a call to event.a.__setitem__(1, 25)
, so Event
knows nothing about it; it is handled entirely by whatever value event.a
resolves to.
If you don't want the assignments in __init__
to trigger the notifications, call super().__setattr__
directly to avoid invoking your override.
def __init__(self):
super().__setattr__('a', [10, 20, 30])
super().__setattr(__('b', 15)
Recently I developed server side on python, I had to detect changes on lists/dictionary/whatever you want, the library that saved my life is traits
. I highly recommend it. you can easly check what changed/removed/added to your attribute.
you can read more here .
specifically for your case, the notification chapter is the most relevant
here's a small snippet I just ran:
from traits.api import *
class DataHandler(HasTraits):
a = List([10, 20, 30])
b = Int(15)
class Event(HasTraits):
def __init__(self):
super().__init__()
self.data_handler = DataHandler()
self.data_handler.on_trait_change(Event._anytrait_changed)
@staticmethod
def _anytrait_changed(obj, name, old, new):
is_list = name.endswith('_items')
if is_list:
name = name[0:name.rindex('_items')]
current_val = getattr(obj, name)
if is_list:
# new handles all the events(removed/changed/added to the list)
if any(new.added):
print("{} added to {} which is now {}".format(new.added, name, current_val))
if any(new.removed):
print("{} removed from {} which is now {}".format(new.removed, name, current_val))
else:
print('The {} trait changed from {} to {} '.format(name, old, (getattr(obj, name))))
e = Event()
e.data_handler.b = 13
e.data_handler.a.append(15)
e.data_handler.a.remove(15)
e.data_handler.a.remove(20)
outupts:
The b trait changed from 15 to 13
[15] added to a which is now [10, 20, 30, 15]
[15] removed from a which is now [10, 20, 30]
[20] removed from a which is now [10, 30]
hope this helps.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.