Bengt Richter wrote: > On 9 Dec 2004 06:11:41 -0800, [EMAIL PROTECTED] (Egil M?ller) wrote: > > >Is there any way to create transparent wrapper objects in Python? > > > >I thought implementing __getattribute__ on either the wrapper class or > >its metaclass would do the trick, but it does not work for the built > >in operators: > > > >class Foo(object): > > class __metaclass__(type): > > def __getattribute__(self, name): > > print "Klass", name > > return type.__getattribute__(self, name) > > def __getattribute__(self, name): > > print "Objekt", name > > return object.__getattribute__(self, name) > > > > > >>>> Foo() + 1 > >Traceback (most recent call last): > > File "<stdin>", line 1, in ? > >TypeError: unsupported operand type(s) for +: 'Foo' and 'int' > >>>> Foo().__add__(1) > >Objekt __add__ > >Traceback (most recent call last): > > File "<stdin>", line 1, in ? > > File "<stdin>", line 8, in __getattribute__ > >AttributeError: 'Foo' object has no attribute '__add__' > > > > > >Thus, note that a + b does not do > > > >try: > > return a.__add__(b) > >except: > > return b.__radd__(a) > > > >and neither, as I first thought > > > >try: > > return type(a).__add__(a, b) > >... > > > >but something along the lines of > > > >try: > > return type.__getattribute__(type(a), '__add__')(a, b) > >... > > > > > >So my naive implementation of a wrapper class, > > > > > >class wrapper(object): > > def __init__(self, value, otherdata): > > self.value = value > > self.otherdata = otherdata > > def __getattribute__(self, name): > > return getattr(self.value, name) > > > > > >does not work. Any ideas for a solution? > > This seems to go some way towards it: > > >>> class MethodDesc(object): > ... def __get__(self, inst, cls=None): > ... if inst is None: return self > ... func = self.__dict__['func'] > ... if func: return func.__get__(self.thing, type(self.thing)) > ... else: return getattr(self.thing, self.name) > ... def __init__(self, name, thing, func): > ... self.name = name > ... self.thing = thing > ... self.func = func > ... def __call__(self, *args, **kw): print 'called %s: %r %r'%(self.name, args, kw) > ... > >>> def wrapit(thing, *otherdata): > ... class Wrapper(object): > ... def __metaclass__(cname, cbases, cdict): > ... for name, func in type(thing).__dict__.items(): > ... if callable(func): > ... if name not in [ > ... '__getattr__', '__getattribute__', '__setattr__', > ... '__new__', '__init__']: > ... cdict[name] = MethodDesc(name, thing, func) > ... else: > ... cdict[name] = MethodDesc(name, thing, None) > ... return type(cname, cbases, cdict) > ... def __init__(self, *otherdata): > ... if otherdata: self.otherdata = otherdata > ... def __getattr__(self, name): > ... raise AttributeError('Wrapped %r object has no attribute %r'% (type(self).__name__, name)) > ... Wrapper.__name__ = 'Wrapped_'+type(thing).__name__ > ... return Wrapper(*otherdata) > ... > >>> class Demo(object): > ... a = 'eigh' > ... two = 2 > ... def foo(self): return 'Demo foo' > ... def bar(self, *args, **kw): print 'Demo bar %r %r'%(args, kw) > ... > >>> o2 = wrapit( Demo(), 'stuff') > >>> o2.foo > <bound method Demo.foo of <__main__.Demo object at 0x02EF184C>> > >>> o2.foo() > 'Demo foo' > >>> o2.bar(1,2,3,x=111,y=222) > Demo bar (1, 2, 3) {'y': 222, 'x': 111} > >>> o2.a > 'eigh' > >>> o2.two > 2 > >>> o2.z > Traceback (most recent call last): > File "<stdin>", line 1, in ? > File "<stdin>", line 16, in __getattr__ > AttributeError: Wrapped 'Wrapped_Demo' object has no attribute 'z' > >>> o2.z = 'zee' > >>> o2.z > 'zee' > >>> o2 > <Wrapped_Demo object at 0x02EF1B6C> > >>> o2.a = 'not eigh' > >>> o2.a > 'not eigh' > >>> del o2.a > >>> o2.a > 'eigh' > >>> o3 = wrapit('strange', 'stuff') > >>> o3 > 'strange' > >>> o3.otherdata > ('stuff',) > >>> o3[2:] > 'range' > >>> > >>> o3.z > Traceback (most recent call last): > File "<stdin>", line 1, in ? > File "<stdin>", line 16, in __getattr__ > AttributeError: Wrapped 'Wrapped_str' object has no attribute 'z' > > Not tested beyond what you see ;-) This doesn't wrap setting attributes on the wrapped object, > so setting attributes sets them on the wrapper itself, but that could be fixed. > > Now what was it you wanted to use a wrapper for? ;-) > Note: > > >>> o3 += 'fail' > >>> o3 > 'strangefail' > >>> type(o3) > <type 'str'> > > So if you want new wrapped instances as results from various ops, we need some more code ;-) > Another time ... (cf. a recent post of mine re wrapping complex as a point type). > > Regards, > Bengt Richter
Ah, thanks. I didn't think of the possibility of creating a list of methods that needed wrapping, and wrapping them uppon creation of the wrapper object. Mainly I think, becaus it seems to me as such an uggly workaround for a misdesign in Python. Also, if the wrapped object gets some extra methods/callable member variables added "after the fact", those will not get wrapped (this is however not a common thing to happen, it just annoys me that it won't work). However, I do have some questions about your implementation: If "if inst is None: return self" to protect for infinite recursion when looking up self.__dict__? What is the reason to give func to the MethodDesc property object, why does its __get__ method have to do if func: return func.__get__(self.thing, type(self.thing)) ? What is the reason to neither wrap, nor just copy, any of __getattr__, __getattribute__, __setattr__, '__new__' or '__init__'? Especially __getattr__, __getattribute__ and __setattr__ seems to need at least some type of wrapping (but perheaps some special one)? Regards, Egil Möller -- http://mail.python.org/mailman/listinfo/python-list