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.
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
**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
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