By definition, a decorator is a function that takes another function and extends the behavior of the latter function without explicitly modifying it. Built-in decorators such as @staticmethod, @classmethod, and @property work in the same way.
How decorator works? Let’s take a look at the following example:
def my_decorator(some_func): def wrapper(): some_func() print("Some function is being called.") return wrapper def another_func(): print("Another function is being called.") foo = my_decorator(another_func) foo()
You would see the following print on the screen:
"Another function is being called." "Some function is being called."
As you can see, we pass some_func
into a closure, and do something before or after calling this function without modifying its original behavior, and we return the function
. As we already learned, python functions are just like other python objects, they are first class objects. The returned function could be called just as any other functions.
@decorator
The above example is already very similar to decorator. The difference is that decorator often comes with a @
symbol. This is an example of python syntax sugar, which often refers to syntax in a programming language that aims to make the things easy to read or to express. For example, the following is an example of a decorator:
@my_decorator def another_func(): print("Another function is being called.")
Decorator that takes any argument
In python, we can use *args
and **kargs
to represent arbitrary arguments. The following example shows how to take arbitrary arguments in a decorator:
def proxy(func): def wrapper(*args, **kargs): return func(*args, **kargs) return wrapper
Decorator with parameters
Sometimes we want the decorator to take parameters, for example, we should implement it in this way:
def decorator(argument): def real_decorator(function): def wrapper(*args, **kwargs): do_something_with_argument(argument) result = function(*args, **kwargs) return wrapper return real_decorator
Decorator tips
One practical tips when defining decorator is to use the functoolss.wraps
, this function would keep all the meta data information of the original functions, including the function signature and docstring information.
import functools def uppercase(func): @functools.wraps(func) def warpper(): return func() return wrapper