English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Decorators accept a function, add some features, and return it. In this article, you will learn how to create decorators and why you should use them.
Python has an interesting feature calledDecorators, which can add functionality to existing code.
This is also calledMetaprogramming,Because part of the program tries to modify another part of the program at compile time.
To understand decorators, we must first understand some basic knowledge of Python.
We must accept the fact that everything in Python isObjects. The names we define are just identifiers bound to these objects.FunctionsThis also applies; they are objects (with properties). Different names can be bound to the same functional object.
This is an example.
def first(msg): print(msg) first("Hello") second = first second("Hello")
When you run the code, the two functions first and second give the same output. Here, the names first and second refer to the same functional object.
Now, doesn't it seem a bit complicated that you can pass a function as a parameter to another function?
If you have used functions like map, filter, and reduce in Python, then you already know this.
This type of function, which takes other functions as parameters, is also calledHigher-order functions. This is an instance of such a function.
def inc(x): return x + 1 def dec(x): return x - 1 def operate(func, x): result = func(x) return result
We call the function as follows.
>>> operate(inc,3) 4 >>> operate(dec,3) 2
In addition, a function can return another function.
def is_called(): def is_returned(): print("Hello") return is_returned new = is_called() # Output "Hello" new()
Here, is_returned() is a nested function that defines and returns each time we call is_drawn().
Finally, we must understandClosure in Python.
Functions and methods are calledCallable,Because they can be called.
In fact, any object that implements the special method __call__() is called callable. Therefore, in the most basic sense, decorators are callable and can return callables.
In essence, decorators accept a function, add some functionality, and return it.
def make_pretty(func): def inner(): print("I am decorated") func() return inner def ordinary(): print("I am an ordinary function")
When running the following code in the shell,
>>> ordinary() I am an ordinary function >>> # Let's decorate this ordinary function >>> pretty = make_pretty(ordinary) >>> pretty() I am decorated I am an ordinary function
In the example shown above, make_pretty() is a decorator. In the assignment step.
pretty = make_pretty(ordinary)
The function ordinary() is decorated, and the returned function is named pretty.
We can see that the decorator function adds some new features to the original function. This is similar to wrapping a gift. The decorator acts as the wrapper. The nature of the item being decorated (the gift inside) does not change. But now, it looks pretty (since it has been decorated).
通常,我们装饰一个函数并将其重新分配为
ordinary = make_pretty(ordinary).
This is a common construction, so Python has syntax to simplify this.
We can use the @ symbol along with the decorator function name and place it above the definition of the function to be decorated. For example,
@make_pretty def ordinary(): print("I am an ordinary function")
is equivalent to
def ordinary(): print("I am an ordinary function") ordinary = make_pretty(ordinary)
This is just syntactic sugar for implementing decorators.
The above decorator is simple and only applies to functions with no parameters. What should we do if our function has parameters as shown below?
def divide(a, b): return a/b
This function has two parameters,aandb. We know that if we willbAn error will occur if passed 0.
>>> divide(2,5) 0.4 >>> divide(2,0) Traceback (most recent call last): ... ZeroDivisionError: division by zero
Now let's make a decorator to check if this will cause an error.
def smart_divide(func): def inner(a, b): print("I want to divide", a, "and", b) if b == 0: print("Oh no! Cannot divide") return return func(a, b) return inner @smart_divide def divide(a, b): return a/b
If an error occurs, this new implementation will return None.
>>> divide(2,5) I want to do division 2 and 5 0.4 >>> divide(2,0) I want to do division 2 and 0 Oops! Can't divide
In this way, we can decorate functions with parameters.
Perceptive observers will notice that the parameters of the nested function inside the inner() decorator are the same as those of the function it decorates. Considering this, now we can make the generic decorator usable with an arbitrary number of parameters.
In Python, this magic is achieved by completing the function(*args, **kwargs).So, args are the positional arguments,tupleInstead of positional arguments, use keywords arguments,dictionary.An example of such a decorator is.
def works_for_all(func): def inner(*args, **kwargs): I can decorate any function return func(*args, **kwargs) return inner
Multiple decorators can be chained in Python.
This means that a function can be decorated multiple times (or the same) with different decorators. We just need to place the decorators above the required function.
def star(func): def inner(*args, **kwargs): print(""*" * 30) func(*args, **kwargs) print(""*" * 30) return inner def percent(func): def inner(*args, **kwargs): print("%%") * 30) func(*args, **kwargs) print("%%") * 30) return inner @star @percent def printer(msg): print(msg) printer("Hello")
This will give the output.
****************************** %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Hello %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ******************************
the above syntax,
@star @percent def printer(msg): print(msg)
is equivalent to
def printer(msg): print(msg) printer = star(percent(printer))
The order of the decorators is important. If we reverse the order,
@percent @star def printer(msg): print(msg)
The execution will occur
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ****************************** Hello ****************************** %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%