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

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.

Python, 66 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
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.

Created by kay schluehr on Thu, 6 Sep 2007 (PSF)
Python recipes (4591)
kay schluehr's recipes (9)

Required Modules

  • (none specified)

Other Information and Tasks