A very slight modification on Tomer Filiba's original Proxy class to use a factory function instead of an instance to create an object proxy the first time it is required. The only other modification is to add an instance variable to LazyLoadProxy to store data specific to the proxy and not the delegated instance.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | class LazyLoadProxy(object):
"""
Modified from http://code.activestate.com/recipes/496741-object-proxying/
"""
__slots__ = ["_obj_fn", "__weakref__", "__proxy_storage"]
def __init__(self, fn, storage=None):
object.__setattr__(self, "_obj_fn", fn)
object.__setattr__(self, "__proxy_storage", storage)
#
# proxying (special cases)
#
def __getattribute__(self, name):
return getattr(object.__getattribute__(self, "_obj_fn")(), name)
def __delattr__(self, name):
delattr(object.__getattribute__(self, "_obj_fn")(), name)
def __setattr__(self, name, value):
setattr(object.__getattribute__(self, "_obj_fn")(), name, value)
def __getitem__(self, index):
return object.__getattribute__(self, "_obj_fn")().__getitem__(index)
def __nonzero__(self):
return bool(object.__getattribute__(self, "_obj_fn")())
def __str__(self):
return str(object.__getattribute__(self, "_obj_fn")())
def __repr__(self):
return repr(object.__getattribute__(self, "_obj_fn")())
def __len__(self):
return len(object.__getattribute__(self, "_obj_fn")())
#
# 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(object.__getattribute__(self, "_obj_fn")(), 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
class Proxy(LazyLoadProxy):
def __init__(self, obj):
super(Proxy, self).__init__(lambda: obj)
|
Object proxying is an important and useful concept in many places. Proxying, but itself, is not very useful... obj
and Proxy(obj)
should behave the same. The necessity of proxying comes to realization when you want Proxy(obj)
to behave slightly different from obj
. You can do it but subclassing the base Proxy class.
As an example for useful proxying, see my ShelfProxy recipe. In this recipe, shelf[key] returns a proxy, not the real object, and the object is serialized back into the shelf when deleted.