I wrote some wrapper which has another object as an attribute. This wrapper proxies (forwards) all attribute requests with __getattr__
and __setattr__
to the object stored as the attribute. What else do I need to provide for my proxy so that the wrapper looks like the wrapped class under usual circumstances?
I suppose I need to fix things like inheritance, maybe __repr__
, ... What else do I need to take care of and how do I fix inheritance so that instanceof()
works?
EDIT: My attempt to make a function proxy, however as I don't understand the recipe fully, it fails :(
setattr_=object.__setattr__ getattr_=object.__getattribute__ class Proxy(object): __slots__=["_func", "_params", "_kwargs", "_obj", "_loaded", "__weakref__"] def __init__(self, func, *params, **kwargs): setattr_(self, "_func", func) setattr_(self, "_params", params) setattr_(self, "_kwargs", kwargs) setattr_(self, "_obj", None) setattr_(self, "_loaded", False) def _get_obj(self): if getattr_(self, "_loaded")==False: print("Loading") setattr_(self, "_obj", getattr_(self, "_func")(*getattr_(self, "_params"), **getattr_(self, "_kwargs"))) setattr_(self, "_loaded", True) return getattr_(self, "_obj") # # proxying (special cases) # def __getattribute__(self, name): return getattr(getattr_(self, "_get_obj")(), name) def __delattr__(self, name): delattr(getattr_(self, "_get_obj")(), name) def __setattr__(self, name, value): setattr(getattr_(self, "_get_obj")(), name, value) def __nonzero__(self): return bool(getattr_(self, "_get_obj")()) def __str__(self): return str(getattr_(self, "_get_obj")()) def __repr__(self): return repr(getattr_(self, "_get_obj")()) # # factories # _special_names=[ '__abs__', '__add__', '__and__', '__call__', '__cmp__', '__coerce__', '__contains__', '__delitem__', '__delslice__', '__div__', '__divmod__', '__eq__', '__float__', '__floordiv__', '__ge__', '__getitem__', '__getslice__', '__gt__', '__hash__', '__hex__', '__iadd__', '__iand__', '__idiv__', '__idivmod__', '__ifloordiv__', '__ilshift__', '__imod__', '__imul__', '__int__', '__invert__', '__ior__', '__ipow__', '__irshift__', '__isub__', '__iter__', '__itruediv__', '__ixor__', '__le__', '__len__', '__long__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__oct__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdiv__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rfloorfiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setitem__', '__setslice__', '__sub__', '__truediv__', '__xor__', 'next', ] @classmethod def _create_class_proxy(cls, theclass): """creates a proxy for the given class""" def make_method(name): def method(self, *args, **kw): return getattr(getattr_(self, "_get_obj")(), name)(*args, **kw) return method namespace={} for name in cls._special_names: if hasattr(theclass, name): namespace[name]=make_method(name) return type("%s(%s)"%(cls.__name__, theclass.__name__), (cls,), namespace) def __new__(cls, obj, *args, **kwargs): """ creates an proxy instance referencing `obj`. (obj, *args, **kwargs) are passed to this class' __init__, so deriving classes can define an __init__ method of their own. note: _class_proxy_cache is unique per deriving class (each deriving class must hold its own cache) """ try: cache=cls.__dict__["_class_proxy_cache"] except KeyError: cls._class_proxy_cache=cache={} try: theclass=cache[obj.__class__] except KeyError: cache[obj.__class__]=theclass=cls._create_class_proxy(obj.__class__) ins=object.__new__(theclass) theclass.__init__(ins, obj, *args, **kwargs) return ins if __name__=='__main__': def t(x, y): print("Running t") return x+y a=Proxy(t, "a", "b") print("Go") print(a+"c")
This problem is reasonably well addressed by this recipe:
Object Proxying (Python recipe)
The general idea you have to follow is that most methods on classes are accessed through some combination of __getattr__
and __getattribute__
either on the class itself, or on its metaclass, but this does not apply to python special methods, the ones that begin and end with double underscore, for those to be found, they must be actual methods on the actual class, no attribute proxying is possible.
Which of those methods you must provide will obviously depend on which of those methods the proxied class itself offers. The special methods you need to provide for isinstance()
are the __instancecheck__
and __subclasscheck__
methods. for repr()
to work, you must also define an appropriate __repr__()
on the proxy class itself.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With