Observer Observable classes in python
Asked Answered
M

1

7

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?

Missilery answered 23/11, 2012 at 11:31 Comment(3)
Python is not Java (luckily). There are no event publisher / subscriber libraries in the standard library, but there are plenty of ways to accomplish this without libraries. Too many in fact, what are you trying to achieve?Huppert
code.activestate.com/recipes/131499-observer-patternMelioration
I'm trying to write a program which acts as a server(say server A) and client at the same time. First the client sends some message to an external server (say server B). Then that server broadcasts messages to our own server. My server (A) should operate my client according to the replies it gets from server B. In that case I need to make my server observable and my client observer. My client acts according to some values in variables in my server. So it should be notified as soon as the values are changed in the my server. How can I achieve such functionality?Missilery
G
8

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!

Giule answered 23/11, 2012 at 12:0 Comment(1)
I don't know what's more pythonic about the third way. When you need the observer pattern you will probably have a bigger architecture than just a list and want to control exactly when and how the observers are notified. And in that regard the first two solutions are better to read, use and maintain imo.Ceja

© 2022 - 2024 — McMap. All rights reserved.