Search code examples
pythonevent-handlingobserver-pattern

How to trigger function on value change?


I realise this question has to do with event-handling and i've read about Python event-handler a dispatchers, so either it did not answer my question or i completely missed out the information.

I want method m() of object A to be triggered whenever value v is changing:

For instance (assuming money makes happy):

global_wealth = 0

class Person()
    def __init__(self):
        self.wealth = 0
        global global_wealth
        # here is where attribute should be
        # bound to changes in 'global_wealth'
        self.happiness = bind_to(global_wealth, how_happy)

    def how_happy(self, global_wealth):
        return self.wealth / global_wealth

So whenever the global_wealth value is changed, all instances of the class Person should change their happiness value accordingly.

NB: I had to edit the question since the first version seemed to suggest i needed getter and setter methods. Sorry for the confusion.


Solution

  • You need to use the Observer Pattern. In the following code, a person subscribes to receive updates from the global wealth entity. When there is a change to global wealth, this entity then alerts all its subscribers (observers) that a change happened. Person then updates itself.

    I make use of properties in this example, but they are not necessary. A small warning: properties work only on new style classes, so the (object) after the class declarations are mandatory for this to work.

    class GlobalWealth(object):
        def __init__(self):
            self._global_wealth = 10.0
            self._observers = []
    
        @property
        def global_wealth(self):
            return self._global_wealth
    
        @global_wealth.setter
        def global_wealth(self, value):
            self._global_wealth = value
            for callback in self._observers:
                print('announcing change')
                callback(self._global_wealth)
    
        def bind_to(self, callback):
            print('bound')
            self._observers.append(callback)
    
    
    class Person(object):
        def __init__(self, data):
            self.wealth = 1.0
            self.data = data
            self.data.bind_to(self.update_how_happy)
            self.happiness = self.wealth / self.data.global_wealth
    
        def update_how_happy(self, global_wealth):
            self.happiness = self.wealth / global_wealth
    
    
    if __name__ == '__main__':
        data = GlobalWealth()
        p = Person(data)
        print(p.happiness)
        data.global_wealth = 1.0
        print(p.happiness)