This pattern describes a way to add or remove behaviour to/from a function dynamically. This is not possible by just stacking decorators. Instead the callables used for the function composition will be hold independently by a Composer object.
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 | class Composer:
'''
The Composer class holds a list of higher order functions. It might b
adapted in various ways.
'''
def __init__(self):
self.callables = []
def weave(composer):
'''
Basic layout for a decorator that composes functions hold independently
in a Composer object.
'''
def wrap(f):
def weaving(*args, **kwd):
g = f
for h in composer.callables:
g = h(g)
return g(*args, **kwd)
weaving.__name__ = f.__name__
return weaving
return wrap
def composite(f):
'''
Used to turn an ordinary function into a higher order function being
composed with its argument. The composite function can be used as a
decorator. But that's not our use pattern here.
'''
def compose(g):
def wrap(*args, **kwd):
return f(g(*args, **kwd))
wrap.__name__ = g.__name__
return wrap
compose.__name__ = f.__name__
return compose
#
#
# Example
#
#
composer = Composer()
@weave(composer)
def foo(x):
return x
def duplicate(x):
if isinstance(x, list):
return x+x
else:
return x
# check it out
>>> foo(1)
1
>>> composer.callables.append(composite(duplicate))
>>> foo(1)
[1,1]
>>> composer.callables.append(composite(duplicate))
>>> foo(1)
[1,1,1,1]
|
The name "weave" is a reminder to AOP and this reminder is not unintentional, since we might weave aspects encoded in functions like duplicate. However in this basic application the Composer object does not examine any signature or name properties of the decorated callables. Composition happens unconditionally and the application programmer is responsible for decoration.
With a little more effort we can enable context dependent programming by sending context information to the Composer, where composition depends on the context.