Python decorator

Breeze and cloud 2021-08-09 12:09:04
python decorator


The front edge of the decorator

Python Decorator for Python Beginners may be a more abstract concept , Before you know about decorators , We must be familiar with Pythond A feature of the method :Python The method in can be passed as a parameter to another method like an ordinary variable , Let's take an example :

def add(x,y):
return x+y
def sub(x,y):
return x-y
def apply(func,x,y):
return func(x,y)
  • Of the above two methods , We define two methods add() and sub() Used for addition and subtraction . In addition, a method is defined apply(), This method is special , Its parameters can receive methods as parameters , The first parameter func Parameters are parameters of the method type ,
  • x,y The two parameters are ordinary parameters , These two parameters will be passed to func() In the method , Final execution func(x,y) Return results . Let's look at this apply Use of methods .
 print(apply(add,3,4)) # Execution results : 7
print(apply(sub,3,4)) # Execution results :-1

As can be seen from the above execution results , By calling apply() Method , We can add and subtract . In terms of execution results , And call this... Alone 2 The execution of two methods has the same effect , But in terms of execution mode, we pass these two methods as parameters to another method for execution .

Problem thinking

Based on the above example , If we add a requirement now : requirement add and sub Print log when method executes , So how do we deal with , Conventional thinking, we may directly add log printing statements to the two methods, as shown below :

def add(x,y):
logging.info('add logging is run' )
return x+y
def sub(x,y):
logging.info('sub logging is run' )
return x-y
  • When there are few methods, there is no problem with this treatment , But when we add other methods that also need to add log printing , In this way, it will appear that the code is very redundant , This is clearly contrary to the fact that life is short , I use Python Of Python The original . So how to solve this problem ?
  • In the case above apply() Method we used it to perform addition and subtraction operations , It is like a common tool to carry the execution of two methods , So now we need to add public content to the method , In fact, it can be transformed directly in this method .
def apply(func,x,y):
logging.info('%s logging is run' %func.__name__)
return func(x,y)
print(apply(add,3,4))
print(apply(sub,3,4))
  • on top apply Method, we added a log statement , In this way, every time you execute add and sub Method will execute the log print statement , So as to reduce in add,sub Method to write log print statements directly , This can streamline the code , Increase of efficiency .
  • It seems that everything is perfect , But there is a problem : Every execution has to pass apply This method performs , It may violate the business logic , For example, I encapsulate a method to call addition , But I have to pass every time apply This transit method is used to execute , It will affect the readability of the code .
  • So is there a way to make add and sub Methods have log printing function , And the best of both worlds method that can be realized by directly calling the original method ? Yes ! At this time, the decorator began to shine !

Preliminary knowledge of decorator

First let's look at an example , Then explain what a decorator is .

import logging
logging.basicConfig(level=logging.INFO,
format='%(asctime)s %(filename)s[line:%(lineno)d]%(levelname)s%(message)s')
def add(x,y):
return x+y
def logging_tool(func): # Log decorator
def wrap(x,y):
logging.info(' %s logging is run' % func.__name__)
return func(x,y)
return wrap
add=logging_tool(add)
print(add(3,4))
  • From the above example, we can see that a method is defined logging_tool Parameters can receive methods An additional method is defined in this method wrap Parameter is 2 A common parameter x,y
  • stay wrap Parameter defines the log print statement , Then return the passed in methods and parameters , Execute the operation result of the original method .
  • Last in logging_tool This method returns wrap Method , This method supports the log printing function and the original method add The summation function of .
  • add=logging_tool(add) In fact, it is a process of decoration assignment ,logging_tool It's a decorator , after logging_tool Method after decoration , new add The method has the function of log printing and summation , We call the decorated... Again add Method add(3,4) The following results can be obtained :
7
2019-07-06 09:08:56,325 decorator_blog.py[line:10]INFO add logging is run

Decorator grammar sugar

stay python I believe that unittest or pytest Students of unit test framework are interested in @ Symbols must not be strange , For example, we often unittest You will see the following similar usage :

@unittest.skip("skip Test2")
class Test2(unittest.TestCase):
def setUp(self):
print("Test2 start")
@classmethod
def setUpClass(cls):
print("Class module start test>>>>>>>>")

above @ The symbol is actually a decorator symbol , It can replace the above add=logging_tool(add) Assignment process . So let's use grammar sugar @ Symbols to transform the code as follows :

import logging
logging.basicConfig(level=logging.INFO,
format='%(asctime)s %(filename)s[line:%(lineno)d]%(levelname)s%(message)s')
def logging_tool(func): # Log decorator
def wrap(x,y):
logging.info(' %s logging is run' % func.__name__)
return func(x,y)
return wrap
@logging_tool
def add(x,y):
return x+y
print(add(3,4))
  • From the above example, we can see that @ Decorative symbols are more concise and clear , It doesn't affect add Method definition structure , It also supports log printing , Improve code reuse efficiency , When a new method needs log printing function , You can also continue to add... At the top of the method @logging_tool that will do .
  • The decorator is essentially a Python A function or class , It allows other functions or classes to add additional functionality without any code changes , The return value of the decorator is also a function / Class object .
  • It is often used in scenarios with faceted requirements , such as : Inserting log 、 Performance testing 、 Transaction processing 、 cache 、 Permission check and other scenarios , Decorators are an excellent design for this type of problem .
  • We have decorators , We can pull out a lot of code that has nothing to do with the function itself into the decorator and continue to reuse . Simply speaking : The function of decorator is to add additional functions to existing objects .

*args **kwargs

  • In the upper decorator, we are based on add The parameters are also in the decorator wrap Method defines two parameters , But if the new business method has three or more parameters, what should be done , Can you define multiple different decorators ? Obviously, this is unreasonable , Here we can use the treatment of indefinite parameters , About *args and **kwargs
  • We'll put the ornament on it loging_tool The code for parameter modification is as follows :
def logging_tool(func): # Log decorator
def wrap(*args,**kwargs):
logging.info(' %s logging is run' % func.__name__)
return func(*args,**kwargs)
return wrap
@logging_tool
def add_multi(x,y,z):
return x+y+z
print(add_multi(1,2,3))

Through the above transformation , But what are the decorated business parameters , We can all receive and process , This greatly improves the flexibility of the decorator .

Decorator parameters

  • Decorators have more flexibility , For example, decorators with parameters , In the decorator call above , The only parameter the decorator receives is the method of executing the business add. If now I want to print logs according to different levels of logs , So how to deal with ? First look at the following code :
def logging_tool(level): # Log decorator
def decorator(func):
def wrap(*args,**kwargs):
if level=='info':
logging.info(' %s info logging is run' % func.__name__)
elif level=='warn':
logging.info(' %s warn logging is run' % func.__name__)
else:
logging.debug(' %s debug logging is run' % func.__name__)
return func(*args,**kwargs) # Return the result of the original business method
return wrap # Return to add the function of decorator
return decorator # Return the whole result
@logging_tool(level="warn")
def add_multi(x,y,z):
return x+y+z
print(add_multi(1,2,3))

Execution results :

6
2019-07-06 10:39:25,698 decorator_blog.py[line:22]INFO add_multi warn logging is run
  • From the above example, we can see , First, the decorator is added with parameters level, Then added a method decorator Used to receive business methods , stay wrap According to the internal decorator parameters level To judge, enter different levels of logs .
  • When calling the decorator , Parameters will also be passed in level=“warn”

functools.wraps

Using decorators greatly reuses code , But he has a disadvantage that the meta information of the original function is missing , For example, functions docstring__name__、 parameter list , Look at the example :

def logging_tool(func): # Log decorator
def wrap(x,y):
'''logging tool wrap'''
logging.info(' %s logging is run' % func.__name__)
return func(x,y)
return wrap
@logging_tool
def add(x,y):
'''
add function
:return:
'''
return x+y
print(add.__name__)
print(add.__doc__)

Execution results :

wrap
logging tool wrap

Through the above example, it is not difficult to find , Original add Methodical docstring__name__、 Properties are all displayed in the decorator wrap The properties of the method replace , Obviously, this is not what we want to see . however , We can go through @functools.wraps Copy the meta information of the original function to the... In the decorator func Function , Make the inside of the decorator func The same meta information as the original function .

import functools
def logging_tool(func): # Log decorator
@functools.wraps(func) # Keep the meta information of the method
def wrap(x,y):
'''logging tool wrap'''
logging.info(' %s logging is run' % func.__name__)
return func(x,y)
return wrap
@logging_tool
def add(x,y):
'''add function'''
return x+y
print(add.__name__)
print(add.__doc__)

Execution results :

add
add function

We can see from the above example , Use @functools.wraps The meta information of the method is retained .

Class decorator

Decorators can be more than functions , It can also be a class , Compared to function decorators , Class decorator has great flexibility 、 High cohesion 、 Encapsulation and other advantages . Using class decorators mainly depends on class call Method , When using @ Form to attach a decorator to a function , This method will be called .

class Foo():
def __init__(self,func):
self._func=func
def __call__(self, *args, **kwargs):
print(' Class decorator starts executing ')
self._func(*args,**kwargs)
print(' Class decorator execution ended ')
@Foo
def bar():
print('hello world')
bar()

Execution results

 Class decorator starts executing
hello world
Class decorator execution ended

Built in decorator

Python Built in some decorators , Such as :@property, @classmethod, @staticmethod Next, let's explain the usage of these built-in decorators one by one .

@property

property Decorators are responsible for turning a method into an attribute ,

class Student(object):
@property
def score(self):
return self._score
@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
s=Student()
s.score=90
print(s.score) # Execution results 90
s.score=101
print(s.score) # Execution results :score must between 0 ~ 100!
s.score='hello'
print(s.score) # Execution results :score must be an integer!

As can be seen from the above example , Use decorators @property We will score Method becomes an attribute of a class , Another pass @score.setter Become an assignable property , If not defined score It's just a read-only property .

@classmethod

@classmethod Class method : Define alternative constructors , The first parameter is the class itself ( Parameter names are not limited , It's usually used cls) We combine the familiar unittest The test framework looks at the following example

import unittest
class Test(unittest.TestCase):
@classmethod # Class method decorator
def setUpClass(cls):
print("test class start .....")
@classmethod
def tearDownClass(cls):
print("test class end ....")
def setUp(self):
print("Test case is testing")
def tearDown(self):
print("test case is end!")
def test_case(self):
print("test_case is runing!")
if __name__=='__main__':
unittest.main()

Execution results

test class start .....
Test case is testing
----------------------------------------------------------------------
test_case is runing!
Ran 1 test in 0.000s
test case is end!
test class end ....
OK
  • In the above case, we use @classmethod To decorate setUpClass and tearDownClass Two methods , These two conveniences become class methods , That is to say Test Class test When a type of use case is executed, it will execute setUpClass and tearDownClass The contents of the two methods .
  • In this way, it is very convenient to perform database initialization and disconnection in practical application scenarios , There is no need to call or write initialization methods separately for each use case method .

@staticmethod

@staticmethod Represents a static method , Static methods differ from ordinary methods in that , Static methods belong to the class , Can be called without instantiation .

class C(object):
@staticmethod
def f():
print('hello world')
C.f() # Running results hello world

Up there we pass @staticmehod Defines a static method f() When calling, you can directly call , There is no need to instantiate .

This article is from WeChat official account. - QA A corner (sutune2020)

The source and reprint of the original text are detailed in the text , If there is any infringement , Please contact the yunjia_community@tencent.com Delete .

Original publication time : 2020-10-12

Participation of this paper Tencent cloud media sharing plan , You are welcome to join us , share .

版权声明
本文为[Breeze and cloud]所创,转载请带上原文链接,感谢
https://pythonmana.com/2021/08/20210809120458667Y.html

  1. 利用Python爬虫获取招聘网站职位信息
  2. Using Python crawler to obtain job information of recruitment website
  3. Several highly rated Python libraries arrow, jsonpath, psutil and tenacity are recommended
  4. Python装饰器
  5. Python实现LDAP认证
  6. Python decorator
  7. Implementing LDAP authentication with Python
  8. Vscode configures Python development environment!
  9. In Python, how dare you say you can't log module? ️
  10. 我收藏的有关Python的电子书和资料
  11. python 中 lambda的一些tips
  12. python中字典的一些tips
  13. python 用生成器生成斐波那契数列
  14. python脚本转pyc踩了个坑。。。
  15. My collection of e-books and materials about Python
  16. Some tips of lambda in Python
  17. Some tips of dictionary in Python
  18. Using Python generator to generate Fibonacci sequence
  19. The conversion of Python script to PyC stepped on a pit...
  20. Python游戏开发,pygame模块,Python实现扫雷小游戏
  21. Python game development, pyGame module, python implementation of minesweeping games
  22. Python实用工具,email模块,Python实现邮件远程控制自己电脑
  23. Python utility, email module, python realizes mail remote control of its own computer
  24. 毫无头绪的自学Python,你可能连门槛都摸不到!【最佳学习路线】
  25. Python读取二进制文件代码方法解析
  26. Python字典的实现原理
  27. Without a clue, you may not even touch the threshold【 Best learning route]
  28. Parsing method of Python reading binary file code
  29. Implementation principle of Python dictionary
  30. You must know the function of pandas to parse JSON data - JSON_ normalize()
  31. Python实用案例,私人定制,Python自动化生成爱豆专属2021日历
  32. Python practical case, private customization, python automatic generation of Adu exclusive 2021 calendar
  33. 《Python实例》震惊了,用Python这么简单实现了聊天系统的脏话,广告检测
  34. "Python instance" was shocked and realized the dirty words and advertisement detection of the chat system in Python
  35. Convolutional neural network processing sequence for Python deep learning
  36. Python data structure and algorithm (1) -- enum type enum
  37. 超全大厂算法岗百问百答(推荐系统/机器学习/深度学习/C++/Spark/python)
  38. 【Python进阶】你真的明白NumPy中的ndarray吗?
  39. All questions and answers for algorithm posts of super large factories (recommended system / machine learning / deep learning / C + + / spark / Python)
  40. [advanced Python] do you really understand ndarray in numpy?
  41. 【Python进阶】Python进阶专栏栏主自述:不忘初心,砥砺前行
  42. [advanced Python] Python advanced column main readme: never forget the original intention and forge ahead
  43. python垃圾回收和缓存管理
  44. java调用Python程序
  45. java调用Python程序
  46. Python常用函数有哪些?Python基础入门课程
  47. Python garbage collection and cache management
  48. Java calling Python program
  49. Java calling Python program
  50. What functions are commonly used in Python? Introduction to Python Basics
  51. Python basic knowledge
  52. Anaconda5.2 安装 Python 库(MySQLdb)的方法
  53. Python实现对脑电数据情绪分析
  54. Anaconda 5.2 method of installing Python Library (mysqldb)
  55. Python implements emotion analysis of EEG data
  56. Master some advanced usage of Python in 30 seconds, which makes others envy it
  57. python爬取百度图片并对图片做一系列处理
  58. Python crawls Baidu pictures and does a series of processing on them
  59. python链接mysql数据库
  60. Python link MySQL database