Proxy Design Advice Needed

nitrogenycs at web.de nitrogenycs at web.de
Wed May 11 17:51:43 EDT 2005


Hello,

I need a way to get a notification whenever a variable of an object
changes. The approach should be non-intrusive so that I can use
existing objects without modifying them.
I want to be notified no matter who or what did change the wrapped
object - even whenever an object internal methods changes the
variables.
So I coded the piece of code shown below (just copy and paste it, it's
ready-to-run).
It's basically a Proxy class that takes an object and whenever somebody
tries to access the proxy, the proxy forwards this to the real object.
Whenever a method of the obj gets called the proxy detects this and the
operation is performed on the the proxy object so that variable change
notifications can be send.
Since I am quite new to Python and the code does not 100% what I want,
I have the feeling, it's not optimal and that there might be a better
way to achieve what I want. For example I am not sure if really all
changes will be catched this way and if the method call re-routed to
proxy object is not a bit hackish. Second, type(Proxy()) doesn't return
the same as type(WrappedObj()). The proxy should behave identical to
the wrapped object. Should I do this with metaclasses to get the type
right or are there better ways? I am not sure if they could fit here or
not.
So, what do you think of this code and how should I improve it?

Thanks a lot for your help!

-Matthias


Code (just copy and pasts and it should run):

import inspect, new, sys

class Proxy(object):
    def __init__(self, wrappedObj):
        # need to call object here to save objs to our own dict
        object.__setattr__(self,'_wrappedObj',wrappedObj)
        object.__setattr__(self,'_observers',{})

    def __getattribute__(self, name):
        # if attribute of proxy obj itself was queried return that
value
        if name in ['_wrappedObj','_observers','Subscribe','Notify']:
            return object.__getattribute__(self, name)
        # otherwise get var from the wrapped object
        attr = getattr( object.__getattribute__(self, '_wrappedObj'),
name )
        # make method use this proxy object instead of wrapped one to
catch updates
        if inspect.ismethod( attr ):
            return new.instancemethod( attr.im_func, self,
attr.im_class )
        else:
            return attr

    def __setattr__(self, name, value):
        # sets attribute of the wrapped value
        setattr(object.__getattribute__(self,'_wrappedObj'), name,
value)
        # notify me of change
        object.__getattribute__(self,'Notify')('Changed',name, value)

    # Adds an observer
    def Subscribe(self, function, event = ''):
        self._observers.setdefault(event,[]).append(function)

    # Notifies all observers
    def Notify(self, event = '', *args):
        for observer in self._observers.get(event, []):
            observer(*args)


class TestObj(object):
    classVar = 'cv'
    def __init__(self):
        self.spam = '1'

    def method(self):
        self.spam = '2'

# create a proxy
p = Proxy(TestObj())

# print some info of it
print 'Proxy: %s ' % p
print 'Class of proxy: %s' % p.__class__
print 'Type of proxy: %s  <--- this sucks' % type(p)
print 'Dir of proxy: %s' % dir(p)

# enable watching changes
p.Subscribe(lambda name, var: sys.stdout.write('%s was changed and is
now %s\n' % (name,var) ) ,'Changed')

# change some stuff
p.method()
p.cv = 'new cv'
p.spam = 1
p.func = lambda x: x-1
print p.func(2)




More information about the Python-list mailing list