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

Decorators can be used to load data structures with a function. Using this technique can reduce the 'lots of declarations; large table definition; startup' structure of some larger programs. The insight is remembering that a decorator can return the original function unchanged.

Python, 31 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
conversions = {}  # the data structure -- a dict-of-dicts

def converter(source, dest):
    '''A decorator that fills the conversions table with entries'''
    def decorate(function):
        '''Update the conversions table and return the original'''
        try:
            previous = conversions[source][dest]
        except KeyError:
            conversions.setdefault(source, {})[dest] = function
            return function
        raise ValueError, 'Two conversions from %r to %r: %r and %r' % (
              source, dest, getattr(previous, '__name__', previous),
              getattr(function, '__name__', function))
    return decorate

@converter('inch', 'feet')
def tofeet(measure):
    return 12 * measure

@converter('feet', 'inch')
def toinch(measure):
    return measure / 12.

# ...
# converter can be used as a non-decorator to load other values.
converter('feet', 'hectare')(None)

print conversions['feet']['inch'](123)
print conversions['inch']['feet'](123)
print conversions['feet']['hectare']

This recipe uses decorators, a feature of python 2.4 or greater.

I hope you get the idea by the code. We avoid defining a lot of conversions and following those declarations by the declaration of a large dictionary. You can certainly use decorators like this to make to make hard-to-read code, but by placing the identifying information with the declaration, you can make some code much clearer.

Admitedly, this _still_ doesn't make the old English system of measurement anything more than barely palatable.

2 comments

Eric Thompson 16 years, 8 months ago  # | flag

Well, hectares aren't an English measure, to be sure... ;)

Ian Bicking 16 years, 8 months ago  # | flag

Generic functions. This is similar to generic functions (PyProtocol's dispatch: http://peak.telecommunity.com/DevCenter/CombiningResults), which might look like:

@dispatch.generic()
def convert(value, source_unit, dest_unit):
    "Convert value from the source to dest units"

@convert.when("source_unit == 'inch' and dest_unit == 'feet'):
def tofeed(v, s, d):
    return v / 12.0

@convert.when("source_unit == 'feet' and dest_unit == 'inch'):
def toinch(v, s, d):
    return v * 12
Created by Scott David Daniels on Mon, 28 Mar 2005 (PSF)
Python recipes (4591)
Scott David Daniels's recipes (10)

Required Modules

  • (none specified)

Other Information and Tasks