Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

alternate ways to implement observer pattern in python

i was going through a post post about how observer pattern can be implemented in python . on the same post there are these comments.

1) In python you may as well just use plain functions, the ‘Observer’ class isnt really needed.

2) This is great example of what Java programmers are trying to do once they switch to Python – they feel like Python is missing all that crap and try to “port” it.

These comments imply that the observer pattern is not really useful in python and there exists other ways to achieve the same effect. is that true and if how can that be done?

Here is the code of observer pattern:

class Observable(object):

    def __init__(self):
        self.observers = []

    def register(self, observer):
        if not observer in self.observers:
            self.observers.append(observer)

    def unregister(self, observer):
        if observer in self.observers:
            self.observers.remove(observer)

    def unregister_all(self):
        if self.observers:
            del self.observers[:]

    def update_observers(self, *args, **kwargs):
        for observer in self.observers:
            observer.update(*args, **kwargs)

from abc import ABCMeta, abstractmethod

class Observer(object):
    __metaclass__ = ABCMeta

    @abstractmethod
    def update(self, *args, **kwargs):
        pass

class AmericanStockMarket(Observer):
    def update(self, *args, **kwargs):
        print("American stock market received: {0}\n{1}".format(args, kwargs))

class EuropeanStockMarket(Observer):
    def update(self, *args, **kwargs):
        print("European stock market received: {0}\n{1}".format(args, kwargs))


if __name__ == "__main__":
    observable = Observable()

    american_observer = AmericanStockMarket()
    observable.register(american_observer)

    european_observer = EuropeanStockMarket()
    observable.register(european_observer)

    observable.update_observers('Market Rally', something='Hello World')
like image 710
anekix Avatar asked Jun 12 '17 12:06

anekix


1 Answers

There are many different ways you can "observe" something in python. Use property descriptors, custom __setattr__, decorators...

Here is a simple example that uses first class functions:

class Foo(object):
    def __init__(self):
        self.observers = []

    def register(self, fn):
        self.observers.append(fn)
        return fn   # <-- See comments below answer

    def notify_observers(self, *args, **kwargs):
        for fn in self.observers:
            fn(*args, **kwargs)

You can then register any callable.

class Bar(object):
    def do_something(self, *args, **kwargs):
        pass # do something

foo = Foo()
bar = Bar()
foo.register(bar.do_something)

This will work properly. The call to do_something will have the correct self value. Because an object's methods are callable objects which carry a reference to the instance they are bound to.

This might help understanding how it works under the hood:

>>> bar
<Bar object at 0x7f3fec4a5a58>
>>> bar.do_something
<bound method Bar.do_something of <Bar object at 0x7f3fec4a5a58>>
>>> type(bar.do_something)
<class 'method'>
>>> bar.do_something.__self__
<Bar object at 0x7f3fec4a5a58>

[edit: decorator example]

You may also use the register method we defined above as a decorator, like this:

foo = Foo()

@foo.register
def do_something(*args, **kwargs):
    pass # do something

For this to work, just remember that register needs to return the callable it registered.

like image 199
spectras Avatar answered Nov 10 '22 13:11

spectras