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

Proxies are usually implemented as objects that forward method calls to a "target" object. This approach has a major problem: forwarding makes the target object the receiver of the method call; this means that calls originating from the body of a method in the target will not go through the proxy (and thus their behavior cannot be modified by the proxy).

For example, suppose we want a proxy to an instance of Target (shown below) that is "safe", i.e., does not do anything bad like firing missiles. We can just define a class that forwards calls to the safe methods, namely send_flowers() and hang_out(). This class can have its own version of fire_missiles() that does nothing. Now consider what happens when we call the proxy object's innocent-looking hang_out() method. The call is forwarded to the target object, which in turn calls the target object's (not the proxy's) fire_missiles() method, and BOOM! (The proxy's version of fire_missiles() is not called because forwarding has made the target object the receiver of the new method call.)

By using delegation, one can implement proxies without the drawbacks of the method-forwarding approach. This recipe shows how Python's __getattr__ method can be used to implement the kind of delegation present in prototype-based languages like Self and Javascript, and how delegation can be used to implement better proxies.

Python, 96 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
90
91
92
93
94
95
96
#!/usr/bin/env python
"""
True Lieberman-style delegation in Python.

Proxies are usually implemented as objects that forward method calls to a
"target" object. This approach has a major problem: forwarding makes the target
object the receiver of the method call; this means that calls originating from
the body of a method in the target will not go through the proxy (and thus their
behavior cannot be modified by the proxy).

For example, suppose we want a proxy to an instance of Target (shown below)
that is "safe", i.e., does not do anything bad like firing missiles. We can
just define a class that forwards calls to the safe methods, namely
send_flowers() and hang_out(). This class can have its own version of
fire_missiles() that does nothing. Now consider what happens when we call
the proxy object's innocent-looking hang_out() method. The call is forwarded
to the target object, which in turn calls the target object's (not the
proxy's) fire_missiles() method, and BOOM! (The proxy's version of
fire_missiles() is not called because forwarding has made the target object
the receiver of the new method call.)

By using delegation, one can implement proxies without the drawbacks of the
method-forwarding approach. This recipe shows how Python's __getattr__
method can be used to implement the kind of delegation present in
prototype-based languages like Self and Javascript, and how delegation can
be used to implement better proxies.
"""

__authors__ = ('Alessandro Warth <awarth@cs.ucla.edu>',
               'Martin Blais <blais@furius.ca>',)


class Target(object):

    def __init__(self, n):
        self.n = n

    def send_flowers(self):
        print 'Sending %d flowers from %s' % (self.n, self)

    def fire_missiles(self):
        print 'Firing %d missiles! from %s' % (self.n, self)

    def hang_out(self):
        # Oops! This is not as innocent as it looks!
        print 'Hang out... not so innocently.'
        self.fire_missiles()

t = Target(17)



"""
Given 't', can we make a proxy to it that avoids firing missiles?
"""

import new
from types import MethodType

class Proxy(object):

    def __init__(self, target):
        self._target = target

    def __getattr__(self, aname):
        target = self._target
        f = getattr(target, aname)
        if isinstance(f, MethodType):
            # Rebind the method to the target.
            return new.instancemethod(f.im_func, self, target.__class__)
        else:
            return f



class SafeProxy(Proxy):
    "Override dangerous methods of the target."
    def fire_missiles(self):
        pass

print '--------'
p = SafeProxy(t)
p.send_flowers()
p.hang_out()




class SafeProxy2(Proxy):
    "Override more methods, wrapping two proxies deep."
    def send_flowers(self):
        print 'Sending MORE and MORE flowers: %s' % self.n

print '--------'
p2 = SafeProxy2(p)
p2.send_flowers()