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