Wrapper objects

Bengt Richter bokr at oz.net
Sat Dec 11 04:30:15 EST 2004


On 10 Dec 2004 15:03:01 -0800, redhog at takeit.se wrote:

>>Well, that could be a feature, depending on what your use case is.
>>Or you could make a method for adding methods, I suppose.
>>A perfectly transparent wrap of obj would be to do nothing ;-)
>>What do you actually want to do?
>
>Actually, the very best would if only type(), isinstance() and the
>is-keyword could an object and a wrapped version of it apart.
>
>I've thought of using a weakdict instaed, problem is, it would be a
>little bit too transparent for my purpose as the only way to tell an
>object and a wrapped version of it apart would be by my own functions
>which would do lookups in that dict. Secondly, it would only allow for
>one comment per object dopy, which would fail horribly for e.g. numbers
>(there are hundreds of uses of the number 1, which all needs different
>comments for the number).
>

Here is a different approach, which I think will perform better than masses
of descriptors and delegations and individual classes, plus wrapping is
just a single assignment. See the W0, W1, W2 usage in the example below.

The idea is to clone the object's class as a subclass in a factory (wrapperclass)
function that takes an example object plus a second argument containing
new or overriding methods and/or class variables. This add-ins argument
is just a class, and everything except what comes in an empty class
(i.e., ['__dict__', '__module__', '__weakref__', '__doc__'] ) is copied
from the dict in to the wrapper class dict, which is initialized as
a clone of the object's class dict.


----< wrapo.py >-----------------------------------------
# wrapo.py -- wrapperclass factory and usage examples
# 20041211 01:02:54 -- bokr
# Use at your own risk. Alpha.

def wrapperclass(obj, AddIns):
    """
    Create a (sub)class which may be assigned to obj.__class__
    to change the obj instance's apparent available methods
    and class variables, taking additions from a supplied
    AddIns class serving as a container for methods and variables.
    """
    cdict = dict(type(obj).__dict__)
    for name, value in AddIns.__dict__.items():
        if name not in ['__dict__', '__module__', '__weakref__', '__doc__']:
            cdict[name] = value
    cname = 'Wrapped_%s_%s'%(AddIns.__name__, type(obj).__name__)
    W = type(cname, (type(obj),), cdict)
    return W

import time
class Wrap_one(object):
    cvar = 'Wrap_one cvar'
    def now(self): return 'W1: '+time.ctime()
    def __repr__(self):
        return 'Wrap_one %r repr:\n    %s'%(self.kw, object.__repr__(self))
    def __add__(self, other):
        return '%r + %r'%(self,str(other))

class Wrap_two(object):
    # let orig cvar alone
    def now(self): return 'W2: '+time.ctime()
    def __repr__(self):
        return 'Wrap_two %r repr:\n    %s'%(self.args, object.__repr__(self))
    def __add__(self, other):
        return '%r + %r'%(type(self).__name__, str(other))

def test():
    class Foo(object):
        cvar = 'orig Foo cvar ;-)'
        def __init__(self, *args, **kw):
            self.args = args
            self.kw =kw
        def now(self): return '(time n/a)'
        def __str__(self):
            return '%s %r\n  %s'%(self.now(), self.cvar, repr(self))
    foo = Foo(1,2,3, x=111,y=222)
    W0 = type(foo)
    W1 = wrapperclass(foo, Wrap_one)
    W2 = wrapperclass(foo, Wrap_two)
    print '--- plain foo:\n', foo
    for w in (W0, W1, W2, W0):
        foo.__class__ = w
        print '\n---- %s ----' % type(foo).__name__
        print foo
        if w!=W0: print foo + ' a %s string.'%w.__name__
    bar = Foo(22,33,bar='this is bar')
    for w in (W0, W1, W2, W0):
        bar.__class__ = w
        print '\n---- %s ----' % type(bar).__name__
        print bar

if __name__ == '__main__':
    test()
---------------------------------------------------------

Result:

[ 1:16] C:\pywk\clp\wrapper>wrapo.py
--- plain foo:
(time n/a) 'orig Foo cvar ;-)'
  <__main__.Foo object at 0x00901630>

---- Foo ----
(time n/a) 'orig Foo cvar ;-)'
  <__main__.Foo object at 0x00901630>

---- Wrapped_Wrap_one_Foo ----
W1: Sat Dec 11 01:16:40 2004 'Wrap_one cvar'
  Wrap_one {'y': 222, 'x': 111} repr:
    <__main__.Wrapped_Wrap_one_Foo object at 0x00901630>
Wrap_one {'y': 222, 'x': 111} repr:
    <__main__.Wrapped_Wrap_one_Foo object at 0x00901630> + ' a Wrapped_Wrap_one_Foo string.'

---- Wrapped_Wrap_two_Foo ----
W2: Sat Dec 11 01:16:40 2004 'orig Foo cvar ;-)'
  Wrap_two (1, 2, 3) repr:
    <__main__.Wrapped_Wrap_two_Foo object at 0x00901630>
'Wrapped_Wrap_two_Foo' + ' a Wrapped_Wrap_two_Foo string.'

---- Foo ----
(time n/a) 'orig Foo cvar ;-)'
  <__main__.Foo object at 0x00901630>

---- Foo ----
(time n/a) 'orig Foo cvar ;-)'
  <__main__.Foo object at 0x00901390>

---- Wrapped_Wrap_one_Foo ----
W1: Sat Dec 11 01:16:40 2004 'Wrap_one cvar'
  Wrap_one {'bar': 'this is bar'} repr:
    <__main__.Wrapped_Wrap_one_Foo object at 0x00901390>

---- Wrapped_Wrap_two_Foo ----
W2: Sat Dec 11 01:16:40 2004 'orig Foo cvar ;-)'
  Wrap_two (22, 33) repr:
    <__main__.Wrapped_Wrap_two_Foo object at 0x00901390>

---- Foo ----
(time n/a) 'orig Foo cvar ;-)'
  <__main__.Foo object at 0x00901390>


Regards,
Bengt Richter



More information about the Python-list mailing list