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')
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With