Proxy Design Advice Needed

N

nitrogenycs

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)
 
V

vincent wehren

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

This may be help:

http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/366254

--
Vincent Wehren
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)
 
N

nitrogenycs

Thanks for the hint so far!
The recipe shown there does not exactly what I want though, it doesn't
do the type() stuff and it hooks up every _ variable which could get
crucial if the wrapped object's methods uses them too.

So I think my question boils down to how I can make type(Proxy) return
type(WrappedObject)?

Any hints?

-Matthias
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top