Python decorator

Ten years in the world 2021-01-23 20:53:00
python decorator


Python Everything is an object , Functions are also objects . Function can be assigned to a variable , A function can be passed as an argument to another function , Functions can be return Statement return function . The decorator is a function that can receive and return functions . That sounds a little bit circuitous at first , But the decorator is essentially a function .
Since we need to learn decorators , The first thing to know is what scene it's used for , Decorators enhance the robustness of code by aspect oriented programming , such as : Log , Handle caching , Authority verification, etc . Next, we will learn step by step Python The usage of ornaments in Chinese .

Let's start with a simple function definition , Function has only one function , Print Hello World:

def hello():
print('Hello World!')

Now there's a new need , To add the logging function before the execution of the original function , So here's the code :

def hello():
print('run hello')
print('Hello World!')

Now the above problem has been solved , Just add a line of code to do it . But the problem is , In the actual work scenario , We may need to change more than just one hello function , It could be 10 individual 、20 At the same time, we need to add the log function . At this time, the problem comes , We're not likely to copy this line of code one function at a time , What's more, it's possible to add more than one line of code at that time , Maybe a hundred lines . And this will cause a lot of duplicate code , When there are too many repetitions in the code , You have to be careful , It's easy to cause unexpected bug, And it's hard to check and maintain .
It's easy to think of a way to define a function to print logs log, Then call it in each function log function :

def log():
print('run hello')
def hello():
log()
print('Hello World!')

It still needs to be repaired hello The code inside the function , It's not that it can't be done , But it's obviously against Opening and closing principle thought —— Close the function code that has been realized , Open to expansion . Although this phrase is often used in object-oriented programming ideas , But functional programming also works .
We can consider solving this problem with higher-order functions , Or to define a log function , But this time it takes a function as an argument , This function performs the function of printing log first , stay log The function finally calls the function that is passed in :

def log(func):
print('run hello')
func()
def hello():
print('Hello World!')
log(hello)

The above code takes advantage of the fact that a function can be passed as a parameter to another function , The problem of modifying the internal code of the original function is solved . It's functional , And it doesn't break the logic inside the original function , But it breaks the code logic of the function caller . in other words , All calls in the original code hello The statement of the function has to start from hello() Change it to log(hello), It seems more troublesome to do so .

Simple decorator

that , Now it's time to introduce Decorator It's time for this concept , Decorator Very good at using Pythonic To solve this kind of problem .
Let's look at the simplest way to write a decorator :

def log(func):
def wrapper():
print('run hello')
func()
return wrapper
def hello():
print('Hello World!')
hello = log(hello)
hello()

This code fully embodies the features of the functions described above , Function can be assigned to a variable , A function can be passed as an argument to another function , Functions can be return Statement return function . current log The function is a Decorator .
So let's define a log function , It takes a function as an argument , And it defines another one inside wrapper function ,wrapper Function after printing the log , Called the passed in func function ( That is to say hello function ), stay log The end of the function returns the internally defined function .
At the bottom of the sample code , We will hello The function is passed as an argument to log function , And assign the returned result to the variable hello, At this time hello What the variable points to is not the original hello function , It is log The decorator returns an internal function wrapper.
Now the caller doesn't have to change the calling method , Still use hello() The way to call hello function , But it's been enhanced , It will automatically execute print('Hello World!') Add the function of printing log before logic .
We have realized the above code from the function Decorator The effect of . But actually ,Python At the grammatical level, it directly supports the decorator pattern . Only one... Is needed @ Symbols make the code more readable , And easy to maintain .

def log(func):
def wrapper():
print('run hello')
func()
return wrapper
@log
def hello():
print('Hello World!')
hello()

@ Symbol is Python The grammatical sugar provided at the grammatical level , But it's essentially equivalent to hello = log(hello).
The above is the most concise one Pythonic Of Decorator , No matter how complicated decorations you encounter in the future , please remember , Its ultimate essence is actually a function , It's just taking advantage of Python The functional features in enable it to handle more complex business scenarios .

The decorated function has arguments 、 Decorators that return values

Actual work scenario , The functions we write tend to be very complex , Want to write a more versatile decorator , There are still some details to be done . But you've learned the essence of decorators , The rest of the examples don't take much effort to understand , You just need to use a decorator with a specific function in a specific scene .

def log(func):
def wrapper(*args, **kwargs):
print('run hello')
return func(*args, **kwargs)
return wrapper
@log
def hello(name):
print('Hello World!')
return f'I am {name}.'
result = hello('xiaoming')
print(result)

*args, **kwargs These two indefinite length parameters , This is a good solution to the problem of the versatility of decorators , Make the decorator decorate any function , Parameters can be passed into the original function as they are .wrapper The function finally calls func The function is preceded by return sentence , Its function is to convert the function of the original function return The result is returned to the caller .

The decorator that holds the meta information of the decorated function

log Inside the decorator wrapper Function print log code print('run hello') It's a fixed string , If we want it to automatically change the print result based on the function name , Such as print(f'run { Function name }.') Form like this .
Each function has one __name__ attribute , Can return its function name :

def hello(name):
print('Hello World!')
print(hello.__name__) # hello

But the problem is that it's used now log After the decorator , The original hello The function already points to wrapper Function , So if you test it, you'll find , Decorated hello function __name__ Attributes have become wrapper, This is obviously not what we want .
We can go through wrapper.__name__ = func.__name__ One line solves this problem , But we have a better way .Python There's a built-in decorator functools.wraps Can help us solve this problem .

from functools import wraps
def log(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f'run {func.__name__}')
return func(*args, **kwargs)
return wrapper
@log
def hello(name):
print('Hello World!')
return f'I am {name}.'
print(hello('xiaoming'))
print(hello.__name__)

The decorator itself has parameters

Maybe you want to control log The log level of the decorator , So it's easy to think of a way to pass parameters to decorators , Let's take a look at an example of a decorator that needs to receive parameters :

from functools import wraps
def log(level):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
if level == 'warn':
print(f'run {func.__name__}')
elif level == 'info':
pass
return func(*args, **kwargs)
return wrapper
return decorator
@log('warn')
def hello(name):
print('Hello World!')
return f'I am {name}.'
result = hello('xiaoming')
print(result)

Compared with previous ornaments , The decorator with parameters has another layer of function nesting , In fact, the effect is like this hello = log('warn')(hello), First call log('warn') Back to the inside decorator function , And then it's equivalent to hello = decorator(hello), In fact, it's the same as decorator without parameters .

Decorator supports both with and without parameters

Sometimes there may be more abnormal needs , It can be used whether the decorator transmits parameters or not , There are many solutions , I'll give you a simple and easy to understand implementation here .

from functools import wraps
def log(level):
if callable(level):
@wraps(level)
def wrapper1(*args, **kwargs):
print(f'run {level.__name__}')
return level(*args, **kwargs)
return wrapper1
else:
def decorator(func):
@wraps(func)
def wrapper2(*args, **kwargs):
if level == 'warn':
print(f'run {func.__name__}')
elif level == 'info':
pass
return func(*args, **kwargs)
return wrapper2
return decorator
@log('warn')
def hello(name):
print('Hello World!')
return f'I am {name}.'
@log
def world():
print('world')
print(hello('xiaoming'))
world()

callable You can determine whether the parameters passed in are callable , But be careful ,callable Only support Python3.2 And above , You can check Official documents Get details .

Class decorator

Compared to function decorators , Class decoration is more flexible , Also more powerful . stay Python Class __call__ Method , Make it self callable without instantiation , And that's when it's done __call__ Internal code .

class Log(object):
def __init__(self, func):
self._func = func
def __call__(self):
print('before')
self._func()
print('after')
@Log
def hello():
print('hello world!')
hello()

Adorner adornment sequence

A function can be decorated by multiple decorators at the same time , So what's the decoration order of multiple decorators ? Now let's explore .

def a(func):
def wrapper():
print('a before')
func()
print('a after')
return wrapper
def b(func):
def wrapper():
print('b before')
func()
print('b after')
return wrapper
def c(func):
def wrapper():
print('c before')
func()
print('c after')
return wrapper
@a
@b
@c
def hello():
print('Hello World!')
hello()

The result of running the above code :

a before
b before
c before
Hello World!
c after
b after
a after

The grammar of multi decoration is equivalent to hello = a(b(c(hello))). According to the print results, it is not difficult to find the execution order of this code . If you know Node.js Of Koa2 The middleware mechanism of framework , Then you will not be unfamiliar with the execution order of the above code , actually Python Decorators follow the same rules Onion model . The code execution sequence of multiple decorators is like peeling an onion , From the outside to the inside , And then from the inside out .
Leave a question for you to think about : The final hello.__name__ Which ornament points to the inside of wrapper Function? ?

The actual battle of decorators

Understand the decorator , We're going to use it , At the beginning of the article, the use of ornaments is mentioned , Let's take a look at an example of using decorators in a real scene .
Flask yes Python Web A very popular micro framework in Ecology , You can go to GitHub Check the source code on the . Here's an example Flask The minimum amount of writing Web application .
ad locum @app.route("/") The function of the decorator is to route the root / The sent request is bound to the handler hello We'll deal with it from above . So when we start Flask Web Server in the future , Visit... At the browser address http://127.0.0.1:5000/ You can get the returned results Hello, World!.

from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello, World!"

Of course , More use scenarios of decorators still need to be explored and discovered by yourself .

The starting address : https://jianghushinian.cn/
版权声明
本文为[Ten years in the world]所创,转载请带上原文链接,感谢
https://pythonmana.com/2021/01/20210123205206122D.html

  1. Experience of learning Python
  2. python7、8章
  3. Chapter 7 and 8 of Python
  4. python bool和str转换
  5. python——循环(for循环、while循环)及练习
  6. python变量和常量命名、注释规范
  7. python自定义异常捕获异常处理异常
  8. python 类型转换与数值操作
  9. python 元组(tuple)和列表(list)区别
  10. 解决python tkinter 与 sleep 延迟问题
  11. python字符串截取操作
  12. Python bool and STR conversion
  13. Python -- loop (for loop, while loop) and Practice
  14. Specification for naming and annotating variables and constants in Python
  15. Python custom exception capture exception handling exception
  16. Python type conversion and numerical operation
  17. The difference between tuple and list in Python
  18. Solve the delay problem of Python Tkinter and sleep
  19. Python string interception operation
  20. Python 100天速成中文教程,GitHub标星7700
  21. Python 100 day quick Chinese course, GitHub standard star 7700
  22. 以我的親身經歷,聊聊學python的流程,同時推薦學python的書
  23. With my own experience, I'd like to talk about the process of learning Python and recommend books for learning python
  24. python爬虫获取起点中文网人气排行Top100(快速入门,新手必备!)
  25. Python crawler to get the starting point of Chinese network popularity ranking Top100 (quick start, novice necessary!)
  26. 【Python常用包】itertools
  27. Itertools
  28. (国内首发)最新python初学者上手练习
  29. (国内首发)最新python初学者上手练习
  30. (first in China) the latest practice for beginners of Python
  31. (first in China) the latest practice for beginners of Python
  32. (数据科学学习手札104)Python+Dash快速web应用开发——回调交互篇(上)
  33. (data science learning notes 104) Python + dash rapid web application development -- callback interaction (Part 1)
  34. (数据科学学习手札104)Python+Dash快速web应用开发——回调交互篇(上)
  35. (data science learning notes 104) Python + dash rapid web application development -- callback interaction (Part 1)
  36. (資料科學學習手札104)Python+Dash快速web應用開發——回撥互動篇(上)
  37. (materials science learning notes 104) Python + dash rapid web application development -- callback interaction (Part 1)
  38. Python OpenCV 图片高斯模糊
  39. Python OpenCV image Gaussian blur
  40. Stargan V2: converse image synthesis for multiple domains reading notes and Python code analysis
  41. 零基础入门Python:基本命令、函数、数据结构
  42. Python: basic commands, functions and data structures
  43. 毫无基础的人如何入门Python?从入门到进阶三份教程,拿走不谢
  44. How can a person without foundation get into Python? From the introduction to the advanced three tutorials, take away
  45. Python设计模式面向对象编程
  46. Python design pattern object oriented programming
  47. Python设计模式面向对象编程
  48. Python design pattern object oriented programming
  49. 怎么样描述你的数据——用python做描述性分析
  50. GitHub上3k+star的python爬虫库你了解吗?详解MechanicalSoup爬虫库
  51. python数据分析——在python中实现线性回归
  52. 疫情来袭,30分钟学会用python开发部署疫情可视化网站
  53. How to describe your data
  54. Do you know the python crawler Library of 3K + star on GitHub? Mechanical soup crawler Library
  55. Python data analysis -- realizing linear regression in Python
  56. When the epidemic strikes, learn to develop and deploy the visualization website of epidemic situation with Python in 30 minutes
  57. 手机上利用python进行数据分析——创建自己的远程jupyter notebook
  58. python数据类型的强制转换
  59. Using Python for data analysis on mobile phones -- creating your own remote jupyter notebook
  60. Mandatory conversion of Python data type