3
The decorates module makes it easy to write decorators. A decorator is
4
an object which adds some property on to another object, while still
5
providing the underlying interface.
10
The decorates function makes a class implement an interface by
11
delegating the implementation to another object. In the case of a class
12
providing an adapter, that object will be the 'context', but it can
13
really be any object stored in an attribute. So while the interfaces use
14
an inheritance mechanism, the classes use a composition mechanism.
16
For example we can define two interfaces IFoo0 <- IFoo...
18
>>> from canonical.lazr import decorates
19
>>> from zope.interface import Interface, Attribute
20
>>> class IFoo0(Interface):
21
... spoo = Attribute('attribute in IFoo0')
23
>>> class IFoo(IFoo0):
26
... baz = Attribute("some attribute")
28
And two classes (BaseFoo0 <- BaseFoo) that do something interesting.
31
... spoo = 'some spoo'
33
>>> class BaseFoo(BaseFoo0):
38
SomeClass can implement IFoo by delegating to an instance of BaseFoo
39
stored in the 'context' attribute. Note that decorates takes the
40
interface as the argument. By default, 'context' is the attribute
41
containing the object to which the interface implementation is
44
>>> class SomeClass(object):
46
... def __init__(self, context):
47
... self.context = context
60
>>> IFoo.providedBy(s)
63
The decorates function takes an optional keyword argument to change
64
attribute containing the object to delegate to. So an existing class,
65
such as SomeOtherClass, can declare the name of the attribute to
68
>>> class SomeOtherClass(object):
69
... decorates(IFoo, context='myfoo')
70
... def __init__(self, foo):
72
... spoo = 'spoo from SomeOtherClass'
75
>>> s = SomeOtherClass(f)
83
'spoo from SomeOtherClass'
92
The decorates() function can only be used in new-style classes. An
93
error is raised when a classic-style class is modified to implement an
96
>>> class SomeClassicClass:
98
Traceback (most recent call last):
100
TypeError: Cannot use decorates() on a classic
101
class: __builtin__.SomeClassicClass.
103
The decorates function cannot be used out side of a class definition,
104
such as in a module or in a function.
107
Traceback (most recent call last):
109
TypeError: Decorates can be used only from a class definition.
114
The Passthrough class is the implementation machinery of decorates.
115
It uses the descriptor protocol to implement the delegation behaviour
116
provided by decorates. It takes two arguments, the name of the attribute
117
that is delegated, and the name of the attribute containing the object
120
To illustrate, p and p2 are two Passthrough instances that use the
121
instance assigned to 'mycontext' to call the 'foo' attribute and
122
the 'clsmethod' method.
124
>>> from canonical.lazr import Passthrough
125
>>> p = Passthrough('foo', 'mycontext')
126
>>> p2 = Passthrough('clsmethod', 'mycontext')
128
Base is a class the implements both 'foo' and 'clsmethod'.
131
... foo = 'foo from Base'
132
... def clsmethod(cls):
134
... clsmethod = classmethod(clsmethod)
136
Adapter is a class that has an instance of Base assigned to the
137
attribute 'mycontext'.
144
>>> adapter = Adapter()
146
The Passthrough instances can get and set their prescribed attributes
147
when passed an instance of adapter.
149
>>> p.__get__(adapter)
152
>>> p.__get__(None, Adapter) is p
155
>>> p2.__get__(adapter)()
158
>>> p.__set__(adapter, 'new value')
162
Passthrough does not implement __delete__. An error is raised if
165
>>> p.__delete__(adapter)
166
Traceback (most recent call last):