Reputation: 309
I've searched around but didn't find anything like this. Let's say I have an army of threads, that keep reading and updating an integer variable x
. I would like a callback for when x
changes over a certain margin, let's say 500, to run the callback.
How can this be done without putting a heavy load on the system, like having a thread that has a while true
and checks if the variable has changed? Performance is critical. But so are ethics.
In plain code would be something like this:
x = 10
def runMe():
print('Its greater than 500!!')
def whenToRun():
return x >= 500
triggers.event(runMe, whenToRun)
Upvotes: 17
Views: 19355
Reputation: 19382
You want to have a function (a "setter") which is called whenever the variable's value changes. A good way to do that is to define a @property
. It will behave like a variable, but will have a getter function and a setter function.
Then, in the setter, call any callbacks you need, which will react to the change.
This should do the trick:
class ObjectHoldingTheValue:
def __init__(self, initial_value=0):
self._value = initial_value
self._callbacks = []
@property
def value(self):
return self._value
@value.setter
def value(self, new_value):
old_value = self._value
self._value = new_value
self._notify_observers(old_value, new_value)
def _notify_observers(self, old_value, new_value):
for callback in self._callbacks:
callback(old_value, new_value)
def register_callback(self, callback):
self._callbacks.append(callback)
Then, you can do:
def print_if_change_greater_than_500(old_value, new_value):
if abs(old_value - new_value) > 500:
print(f'The value changed by more than 500 (from {old_value} to {new_value})')
holder = ObjectHoldingTheValue()
holder.register_callback(print_if_change_greater_than_500)
holder.value = 7 # nothing is printed
holder.value = 70 # nothing is printed
holder.value = 700 # a message is printed
Upvotes: 33
Reputation: 366103
If x
is an attribute of some object, rather than a global variable, this is very simple: add a __setattr__
method:
class MyType:
def __setattr__(self, name, value):
if name == 'x':
self.x_callback(value)
super().__setattr__(name, value)
Obviously there are ways you can make this more flexible:
x_callback
.register(name, callback)
whatever you want.register(name, condition, callback)
.self.x_callback(oldval=self.x, newval=value)
so the callback can see old and new values.if self.x_callback(value):
so the callback can accept or reject the change. value = self.x_callback(value)
so the callback can override the change.Upvotes: 7