Welcome, guest | Sign In | My Account | Store | Cart

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.

Python, 89 lines
 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.

Created by Cory Virok on Fri, 13 Jan 2012 (PSF)
Python recipes (4591)
Cory Virok's recipes (1)

Required Modules

  • (none specified)

Other Information and Tasks