Wrapper objects
redhog at takeit.se
redhog at takeit.se
Fri Dec 10 12:33:51 EST 2004
Bengt Richter wrote:
> On 9 Dec 2004 06:11:41 -0800, redhog at takeit.se (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
More information about the Python-list
mailing list