Completely wrap an object in Python
The simplest way in most cases is probably:
class ObjectWrapper(BaseClass): def __init__(self, baseObject): self.__class__ = type(baseObject.__class__.__name__, (self.__class__, baseObject.__class__), {}) self.__dict__ = baseObject.__dict__ def overriddenMethod(self): ...
Working in this way, i.e. by reassigning self's __class__
and __dict__
in this fashion, you need only provide your overrides -- Python's normal attribute getting and setting mechanisms will do the rest... mostly.
You'll be in trouble only if baseObject.__class__
defines __slots__
, in which case the multiple inheritance approach doesn't work and you do need the cumbersome __getattr__
(as others said, at least you don't need to worry that it will be called with attributes you're overriding, as it won't!-), __setattr__
(a greater pain, as it DOES get called for every attribute), etc; and making isinstance
and special methods work takes painstaking and cumbersome detailed work.
Essentially, __slots__
means that a class is a special, each instance a lightweight "value object" NOT to be subject to further sophisticated manipulation, wrapping, etc, because the need to save a few bytes per instance of that class overrides all the normal concerns about flexibility and so on; it's therefore not surprising that dealing with such extreme, rare classes in the same smooth and flexible way as you can deal with 99%+ of Python objects is truly a pain. So DO you need to deal with __slots__
(to the point of writing, testing, debugging and maintaining hundreds of lines of code just for those corner cases), or will the 99% solution in half a dozen lines suffice?-)
It should also be noted that this may lead to memory leaks, as creating a subclass adds the subclass to the base class' list of subclasses, and isn't removed when all instances of the subclass are GC'd.
Please look at http://code.activestate.com/recipes/577555-object-wrapper-class/ for the complete code, including important comments. It boils down to:
class Wrapper(object): def __init__(self, obj): self._wrapped_obj = obj def __getattr__(self, attr): if attr in self.__dict__: return getattr(self, attr) return getattr(self._wrapped_obj, attr)
__getattr__
has the advantage that it's only called when the attribute does not exist, so you should not need an explicit list -- anything you don't define will automatically get proxied.
__setattr__
is trickier because it's always called. Make sure you use a superclass call or object.__setattr__
when setting your own attributes; using setattr()
within __setattr__
will cause infinite recursion.
The final bit, affecting isinstance, is very difficult. You can do it with an assigment to your wrapper instance's .__class__
variable (but this also overrides class dictionary resolution order), or by dynamically constructing your wrapper type using a metaclass. Since isinstance is so rare in Python code, it seems overkill to actually try to trick it.
More information on special attribute access methods.
Even more information on them, plus some help with metaclasses.