The corrosion of Aaron Stone

about

Python Meta Decorators

Recently, I was working on a project in Python framework that was built around decorators. Each of my functions needed something like six decorators, each with its own arguments, but mostly all the same. I thought, “This is a classic case of cut-and-paste coding; there has to be a better way!” I toiled over decorators for a while, and finally came up with this pattern:

def monkey(arg):
  def outer(f):
    def inner(*args, **kwargs):
      print "Monkey: %s" % arg
      f(*args, **kwargs)
    return inner
  return outer

def zombie(arg):
  def outer(f):
    def inner(*args, **kwargs):
      print "Zombie: %s" % arg
      f(*args, **kwargs)
    return inner
  return outer

def wrapper(m, z):
  def outer(f):
    @monkey(m)
    @zombie(z)
    def inner(*args, **kwargs):
      f(*args, **kwargs)
    return inner
  return outer

@wrapper("coconut", "brains")
def mytesta(foo):
  print foo

@wrapper("bacon", "chainsaw")
def mytestb(foo):
  print foo

mytesta("Hello World")
mytestb("Hello World")

What I’m doing here is first setting up two standard argument-taking decorators, @monkey and @zombie. Then I’m setting up a decorator that returns a decorated function, whose arguments are built at decoration time. This lets me template out a complex decoration pattern where only a few arguments need to change for each function, and pass just those arguments into my meta-decorator.

This is the output:

Monkey: coconut
Zombie: brains
Hello World

Monkey: bacon
Zombie: chainsaw
Hello World