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