Ken Kinder's personal website

Decorating your methods

You can read about Python Decorators in the PEP like I had to, or you can just skip to the good stuff.

Python decorators are a way of modifying a function, of quickly wrapping it. They’re sort of like aspect-oriented programming, in that they let you define logic that cross-cuts methods all over your program. Here’s an example that takes a method that returns a list, and converts that list to a CSV file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import csv
import StringIO
 
def list2csv(func):
    def wrapper(*args, **kwargs):
        listValue = func(*args, **kwargs)
        fd = StringIO.StringIO()
        writer = csv.writer(fd)
        for row in listValue:
            writer.writerow(row)
        return fd.getvalue()
    return wrapper
 
@list2csv
def getList():
    return [
        ('First Name', 'Last Name',  'Profession'),
        ('Ken',        'Kinder',     'Software Engineer'),
        ('John',       'Stewart',    'Funny man'),
        ('Guido',      'Van Russum', 'Smart guy')
        ]
 
print getList()

Results in…

First Name,Last Name,Profession
Ken,Kinder,Software Engineer
John,Stewart,Funny man
Guido,Van Russum,Smart guy

Now, you may have seen a few decorators that take keyword arguments, and look like this:

1
2
3
4
5
6
7
8
@list2csv(dialect=csv.exel)
def getList():
    return [
        ('First Name', 'Last Name',  'Profession'),
        ('Ken',        'Kinder',     'Software Engineer'),
        ('John',       'Stewart',    'Funny man'),
        ('Guido',      'Van Russum', 'Smart guy')
        ]

If you’re like me, you’re tempted to just add a keyword argument, dialect, to the list2csv method. But what’s important to keep in mind is that the text after the @ in your decoration is evaluated to being the decorator. In other words, @list2csv is not a shortcut to @list2csv(). If list2csv is called, it should return a decorator itself. Here’s how:

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
import csv
import StringIO
 
class Tabs(csv.excel):
    delimiter = '\t'
 
def list2csv(func=None, dialect='excel'):
    if func is None:
        return lambda _: list2csv(_, dialect)
 
    def wrapper(*args, **kwargs):
        listValue = func(*args, **kwargs)
        fd = StringIO.StringIO()
        writer = csv.writer(fd, dialect)
        for row in listValue:
            writer.writerow(row)
        return fd.getvalue()
    return wrapper
 
@list2csv(dialect=Tabs)
def getList():
    return [
        ('First Name', 'Last Name',  'Profession'),
        ('Ken',        'Kinder',     'Software Engineer'),
        ('John',       'Stewart',    'Funny man'),
        ('Guido',      'Van Russum', 'Smart guy')
        ]
 
print getList()

This is a trick that lets you pass keyword arguments to the decorator. Notice lines 8 and 9, then 20. When this method is the called with a function, it behaves as normal, otherwise it returns a lambda to itself, with the arguments already specified.

1 Comment to Decorating your methods

  1. April 9, 2006 at 4:15 PM | Permalink

    Insofar as I don’t know what this means, I am inclined to make a bad joke. :-) Instead I think I will go take a nap. Hi, Ken!

Leave a Reply

You can use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">