Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Observer Observable classes in python

Tags:

python

How do I write a program with observer/ observable facilities in Python as in Java? I'd write something like following in Java.

import java.util.Observable;
import java.util.Observer;


public class ObservDemo extends Object {
  MyView view;
  MyModel model;

  public ObservDemo() {

    view = new MyView();
    model = new MyModel();
    model.addObserver(view);

  }

  public static void main(String[] av) {
    ObservDemo me = new ObservDemo();
    me.demo();
  }

  public void demo() {
    model.changeSomething();
  }

  /** The Observer normally maintains a view on the data */
  class MyView implements Observer {
    /** For now, we just print the fact that we got notified. */
    public void update(Observable obs, Object x) {
      System.out.println("update(" + obs + "," + x + ");");
    }
  }

  /** The Observable normally maintains the data */
  class MyModel extends Observable {
    public void changeSomething() {
      // Notify observers of change
      setChanged();
      notifyObservers();
    }
  }
}

(This code is take from the following link http://www.java2s.com/Code/Java/Design-Pattern/AsimpledemoofObservableandObserver.htm )

how do I accomplish in such thing in Python?

like image 901
maheshakya Avatar asked Nov 23 '12 11:11

maheshakya


1 Answers

First of all, as Martijn Pieters said, Python is not Java. This means that probably you don't need the whole observer/observed pattern, but can boil it down to a more simplified version. In the end I will show something a little more pythonesque, but to remain to a very basic java implementation, you can try something like this:

class Observer(object):
    def notify(self,*args,**kwargs):
        print args,kwargs

class Target(object):
    def __init__(self,*observers):
        self.observes = observers

    #this notify for every access to the function
    def event(self,data):
        for obs in self.observes:
            obs.notify('event',data)
        print "event with",data

t = Target(Observer())
t.event(1)
#('event', 1) {}
#event with 1

otherwise you can implement it with a decorator, that is quite similar:

def observe(f):
    def decorated(self,*args,**kwargs):
        for obs in self.observes:
            obs.notify('event',*args,**kwargs)
        return f(self,*args,**kwargs)
    return decorated

class Target(object):
    def __init__(self,*observers):
        self.observes = observers

    @observe
    def otherevent(self,data):
        print "other event with",data

Now, all these methods works, but they are not very pythonic. The best way that comes to mind to implement something like this in a pythonic way is to implement a wrapper that check for attribute access and call a callback function (that can be the notify function of the observer, but it's a more general approach)

class Wrapper(object):
    def __init__(self,wrapped,*callbacks):
        self.wrapped = wrapped
        self.callbacks = callbacks

    def __getattr__(self,name):
        res = self.wrapped.__getattribute__(name)
        if not callable(res):
            return res
        def wrap(*args,**kwargs):
            for c in self.callbacks:
                c(self.wrapped,f,*args,**kwargs)
            return res(*args,**kwargs)
        return wrap

    def __str__(self):
        return self.wrapped.__str__()

#in this example I will keep a record of each call performed on a list
called = []
#this is the list
a = []
#and this is the wrapped list
w = Wrapper(a,lambda f,v,ign: called.append((f,v)) )
#I append an element to the wrapper
w.append(1)
#and I can see that it modify the original list
print a
#the print of the wrapped is well behaved, having defined the __str__ function
print w
#and we can see which function we called and which were the parameters
print called

This approach is alittle more convoluted, as you have to redirect manually all the magic methods, but is way more powerful as allow to implement the observer pattern to any kind of object, attaching to it any compatible function, without the need to specify a generic class of observes. There are ways to automaticaly redirect all the magic function calls, but it's a little complicated and will just confuse the main point

The fastest you forget about java when working in python, the more fun it will get. I suggest you to read this piece:

http://dirtsimple.org/2004/12/python-is-not-java.html

good luck with your work!

like image 141
EnricoGiampieri Avatar answered Oct 22 '22 04:10

EnricoGiampieri