Wrapper objects

Bengt Richter bokr at oz.net
Thu Dec 9 21:11:13 EST 2004


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



More information about the Python-list mailing list