Python Decorators
With a decorator, you can give a function extra behavior and leave its code untouched.
A decorator is itself a function. It receives a function as input and gives back a new one.
Basic Decorator
First define the decorator, then write @decorator_name on the line above the function you want to apply it to.
In the example, the @changecase line sitting right above myfunction "decorates" it with changecase.
So changecase is the decorator.
And myfunction is the function being decorated.
Example
This decorator converts whatever the decorated function returns to upper case.
changecase that converts a function's return value to uppercase.
Apply it to a function that returns "Hello Sally" and print the result.
Output: complete Python program
def changecase(func):
def myinner():
return func().upper()
return myinner
@changecase
def myfunction():
return "Hello Sally"
print(myfunction())
Multiple Decorator Calls
One decorator can be reused as often as you like. Simply put it above each function that needs it.
Example
The same @changecase decorator applied to two functions:
changecase that uppercases return values.
Apply it to two different functions and print both results.
Output: complete Python program
def changecase(func):
def myinner():
return func().upper()
return myinner
@changecase
def myfunction():
return "Hello Sally"
@changecase
def otherfunction():
return "I am speed!"
print(myfunction())
print(otherfunction())
Arguments in the Decorated Function
A function that needs arguments can be decorated too, as long as the wrapper function passes those arguments along:
Example
Decorating a function that takes an argument:
changecase decorator that works with a function taking one argument.
The decorated function should return an uppercased greeting.
Output: complete Python program
def changecase(func):
def myinner(x):
return func(x).upper()
return myinner
@changecase
def myfunction(nam):
return "Hello " + nam
print(myfunction("John"))
*args and **kwargs
A decorator can't always know which arguments the decorated function will receive. The solution is to give the wrapper function the parameters (*args, **kwargs). It can then take any number of arguments of any type and forward them to the decorated function.
Example
Making the wrapper safe with *args and **kwargs:
changecase decorator using *args and **kwargs for flexibility.
Apply it to a function that takes a name parameter.
Output: complete Python program
def changecase(func):
def myinner(*args, **kwargs):
return func(*args, **kwargs).upper()
return myinner
@changecase
def myfunction(nam):
return "Hello " + nam
print(myfunction("John"))
Decorator With Arguments
To give a decorator arguments of its own, wrap it in one more level of function.
Example
A decorator factory: the argument it receives decides which casing the output gets.
changecase that takes an argument n.
If n==1, lowercase the result; otherwise uppercase it.
Output: complete Python program
def changecase(n):
def changecase(func):
def myinner():
if n == 1:
a = func().lower()
else:
a = func().upper()
return a
return myinner
return changecase
@changecase(1)
def myfunction():
return "Hello Linus"
print(myfunction())
Multiple Decorators
One function can have several decorators.
Simply stack the decorator calls one above the other.
They are applied in reverse order, with the decorator nearest the function going first.
Example
One decorator adds a greeting, and the other converts the text to upper case:
changecase (uppercase) and addgreeting (adds greeting text).
Stack both on a function and print the result.
Output: complete Python program
def changecase(func):
def myinner():
return func().upper()
return myinner
def addgreeting(func):
def myinner():
return "Hello " + func() + " Have a good day!"
return myinner
@changecase
@addgreeting
def myfunction():
return "Tobias"
print(myfunction())
Preserving Function Metadata
Every Python function carries metadata, which you can read through its __name__ and __doc__ attributes.
Decorating a function loses that metadata, though, and the original values are no longer available.
The standard library has a fix: the functools.wraps function keeps the original function's name and docstring in place.
Example
An undecorated function reports its own name through __name__:
__name__ attribute.
Output: complete Python program
def myfunction():
return "Have a great day!"
print(myfunction.__name__)
Example
Ask a decorated function for its name and the answer is different:
__name__.
Print the __name__ of a decorated function.
Output: complete Python program
def changecase(func):
def myinner():
return func().upper()
return myinner
@changecase
def myfunction():
return "Have a great day!"
print(myfunction.__name__)
Example
With functools.wraps imported and applied, the original name and docstring survive.
functools.wraps to preserve the original function's name when decorating.
Print the __name__ of the decorated function.
Output: complete Python program
import functools
def changecase(func):
@functools.wraps(func)
def myinner():
return func().upper()
return myinner
@changecase
def myfunction():
return "Have a great day!"
print(myfunction.__name__)