That's a very interesting question ! Another one has already been answered in depth on the basic usage of decorators. But it does not provide much insight on modifying arguments
Stackable decorators
You can find on that other question an example of stacked decorators with the following piece of explanation hidden in a very, very long and detailed answer :
Yes, that’s all, it’s that simple. @decorator is just a shortcut to:
another_stand_alone_function = my_shiny_new_decorator(another_stand_alone_function)
And that's the magic. As python documentation states : a decorator is a function returning another function.
That means you can do :
from functools import wraps
def decorator1(f):
@wraps(f)
def wrapper(*args, **kwargs):
do_something()
f(*args, **kwargs)
return wrapper
def decorator2(f):
@wraps(f)
def wrapper(*args, **kwargs):
do_something_else()
f(*args, **kwargs)
return wrapper
@decorator1
@decorator2
def myfunc(n):
print "."*n
#is equivalent to
def myfunc(n):
print "."*n
myfunc = decorator1(decorator2(myfunc))
Decorators are not Decorators
Python decorators might be puzzling for developpers who learned OOP with a language where GoF has already used half of the dictionary to name the patterns who fix the failures of the language is the de-facto design pattern shop.
GoF's decorators are subclasses of the component (interface) they're decorating, therefore sharing this interface with any other subclass of that component.
Python decorators are functions returning functions (or classes).
Functions all the way down
A python decorator is a function returning a function, any function.
Most decorators out there are designed to extend the decorated function without getting in the way of it's expected behavior. They are shaped after GoF's definition of the Decorator pattern, which describes a way to extend an object while keeping it's interface.
But GoF's Decorator is a pattern, whereas python's decorator is a feature.
Python decorators are functions, these functions are expected to return functions (when provided a function).
Adapters
Let's take another GoF pattern : Adapter
An adapter helps two incompatible interfaces to work together.
This is the real world definition for an adapter.
[An Object] adapter contains an instance of the class it wraps.
In this situation, the adapter makes calls to the instance of the wrapped
object.
Take for example an object — say a dispatcher, who would call a function which takes some defined parameters, and take a function who would do the job but provided another set of parameters. Parameters for the second function can be derived from those of the first.
A function (which is a first-class object in python) who would take the parameters of the first and derive them to call the second and return a value derived from its result would be an adapter.
A function returning an adapter for the function it is passed would be an adapter factory.
Python decorators are functions returning functions. Including adapters.
def my_adapter(f):
def wrapper(*args, **kwargs):
newargs, newkwargs = adapt(args, kwargs)
return f(*newargs, **newkwargs)
@my_adapter # This is the contract provider
def myfunc(*args, **kwargs):
return something()
Oooooh, I see what you did there… is it good style ?
I'd say, hell yeah, yet another built-in pattern ! But you'd have to forget about GoF Decorators and simply remember that python decorators are functions which return functions. Therefore, the interface you're dealing with is the one of the wrapper function, not the decorated one.
Once you decorate a function, the decorator defines the contract, either telling it's keeping the interface of the decorated function or abstracting it away. You don't call that decorated function anymore, it's even tricky to try it, you call the wrapper.