Python 装饰器记录总结 (终极版)

举报
风吹稻花香 发表于 2021/06/05 00:19:54 2021/06/05
【摘要】 Python 装饰器记录总结 (终极版) 原文链接:http://magicroc.com/2017/04/10/Python装饰器记录总结/ 装饰器是一个函数,一个用来包装函数的函数,装饰器在函数申明(不需要调用)完成的时候被调用,调用之后返回一个修改之后的函数对象,将其重新赋值原来的标识符,并永久丧失对原始函数对象的访问。对某个方法应用了装饰方法后, ...

Python 装饰器记录总结 (终极版)

装饰器是一个函数,一个用来包装函数的函数,装饰器在函数申明(不需要调用)完成的时候被调用,调用之后返回一个修改之后的函数对象,将其重新赋值原来的标识符,并永久丧失对原始函数对象的访问。对某个方法应用了装饰方法后, 其实就改变了被装饰函数名称所引用的函数代码块入口点,使其重新指向了由装饰方法所返回的函数入口点。

配图

无参数装饰器-包装无参数函数

这是一个打印log的decorator,此时输出了函数名、返回值、运行时间。


   
  1. import time
  2. from functools import wraps
  3. def log(func):
  4. @wraps(func)
  5. def wrapper():
  6. print("function runing")
  7. ts = time.time()
  8. result = func()
  9. te = time.time()
  10. print("function = {0}".format(func.__name__))
  11. print(" return = {0}".format(result))
  12. print(" time = %.6f sec" % (te - ts))
  13. return wrapper
  14. @log
  15. def sum():
  16. x = 1
  17. y = 2
  18. return x + y
  19. sum()

运行结果:


   
  1. function runing
  2. function = sum
  3. return = 3
  4. time = 0.000001 sec

代码中在sum函数上一行添加@log相当于执行了语句:

sum = log(sum)

  

由于log()是一个decorator,返回一个函数,所以,原来的sum()函数仍然存在,只是现在同名的sum变量指向了新的函数,于是调用sum()将执行新函数,即在log()函数中返回的wrapper()函数

无参数装饰器-包装有参数函数


   
  1. import time
  2. from functools import wraps
  3. def log(func):
  4. @wraps(func)
  5. def wrapper(*args, **kwargs): # 接受传入的参数
  6. print("function runing")
  7. ts = time.time()
  8. result = func(*args, *kwargs) # 函数使用传入的参数
  9. te = time.time()
  10. print(" function = {0}".format(func.__name__))
  11. print("arguments = {0} {1}".format(args, kwargs))
  12. print(" return = {0}".format(result))
  13. print(" time = %.6f sec" % (te - ts))
  14. return wrapper
  15. @log
  16. def sum(x,y):
  17. return x + y
  18. sum(1,2)

运行结果:


   
  1. function runing
  2. function = sum
  3. arguments = (1, 2)
  4. return = 3
  5. time = 0.000004 sec

带参数装饰器 – 包装无参数函数


   
  1. import time
  2. from functools import wraps
  3. def log(name):
  4. def decora(func):
  5. @wraps(func)
  6. def wrapper():
  7. print("name : {0}".format(name))
  8. print("function runing")
  9. ts = time.time()
  10. result = func()
  11. te = time.time()
  12. print(" function = {0}".format(func.__name__))
  13. print(" return = {0}".format(result))
  14. print(" time = %.6f sec" % (te - ts))
  15. return wrapper
  16. return decora
  17. @log('MagicRoc')
  18. def sum():
  19. x = 1
  20. y = 2
  21. return x + y
  22. sum()

运行结果:


   
  1. name : MagicRoc
  2. function runing
  3. function = sum
  4. return = 3
  5. time = 0.000002 sec

此时代码中在sum函数上一行添加@log(‘MagicRoc’)相当于执行了语句:

sum = log('MagicRoc')(sum)

  

首先执行log(‘MagicRoc’),返回的是decorator函数,再调用返回的函数,参数是sum函数,返回值最终是wrapper函数。

不同在于:比上一层多了一层封装,先传递参数,再传递函数名

带参数装饰器 – 包装有参数函数

  
import time
from functools import wraps
def log(name): def decora(func): @wraps(func) def wrapper(*args, **kwargs): print("name : {0}".format(name)) print("function runing") ts = time.time() result = func(*args, **kwargs) te = time.time() print(" function = {0}".format(func.__name__)) print("arguments = {0} {1}".format(args, kwargs)) print("   return = {0}".format(result)) print(" time = %.6f sec" % (te - ts)) return wrapper return decora

@log('log')
def sum(x,y): return x + y

sum(1,2)

运行结果:


   
  1. name : MagicRoc
  2. function runing
  3. function = sum
  4. arguments = (1, 2) {}
  5. return = 3
  6. time = 0.000002 sec

多个Decrorator


   
  1. from functools import wraps
  2. def div(cla):
  3. def decara(func):
  4. @wraps(func)
  5. def wrapper(*args,**kwargs):
  6. return "<div class = %s > %s </div>" % (cla, func())
  7. return wrapper
  8. return decara
  9. def h1(cla):
  10. def decara(func):
  11. @wraps(func)
  12. def wrapper(*args,**kwargs):
  13. return "<h1 class = %s > %s </h1>" % (cla,func())
  14. return wrapper
  15. return decara
  16. @div('divclass')
  17. @h1('h1class')
  18. def hello():
  19. return 'hello world'
  20. print( hello())

装饰器的顺序很重要:


   
  1. @A
  2. @B
  3. @C
  4. def f ():

等价于:

f = A(B(C(f)))

  

代码运行过程中中hello先指向了h1中的wrapper,又指向了div中的wrapper。
h1中的func指向的是要修饰的函数本身,而div中的func指向的是h1中的wrapper函数。

装饰器类


   
  1. from functools import wraps
  2. import time
  3. class log(object):
  4. def __init__(self,name):
  5. self.name = name
  6. def __call__(self,func):
  7. @wraps(func)
  8. def wrapper(*args, **kwargs):
  9. print("name : {0}".format(self.name))
  10. print("function runing")
  11. ts = time.time()
  12. result = func(*args, **kwargs)
  13. te = time.time()
  14. print(" function = {0}".format(func.__name__))
  15. print("arguments = {0} {1}".format(args, kwargs))
  16. print(" return = {0}".format(result))
  17. print(" time = %.6f sec" % (te - ts))
  18. return wrapper
  19. @log('MagicRoc')
  20. def sum():
  21. x = 1
  22. y = 2
  23. return x + y
  24. sum()

将装饰器定义为类的一部分

用类方法作为装饰器函数和普通函数作为装饰器函数极其相似


   
  1. from functools import wraps
  2. class A:
  3. def decorator(self, func):
  4. @wraps(func)
  5. def wrapper(*args, **kwargs):
  6. return func(*args, **kwargs)
  7. return wrapper

使用的时候:


   
  1. a = A()
  2. @a.decorator
  3. def fun():
  4. pass

Flask 通过URL的路由来调用相关注册的函数就是将装饰器定义为类的一部分

理解Flask 路由注册回调函数


   
  1. class MyApp():
  2. def __init__(self):
  3. self.func_map = {}
  4. def register(self, name):
  5. def func_wrapper(func):
  6. self.func_map[name] = func
  7. return func
  8. return func_wrapper
  9. def call_method(self, name=None):
  10. func = self.func_map.get(name, None)
  11. if func is None:
  12. raise Exception("No function registered against - " + str(name))
  13. return func()
  14. app = MyApp()
  15. @app.register('/')
  16. def main_page_func():
  17. return "This is the main page."
  18. @app.register('/next_page')
  19. def next_page_func():
  20. return "This is the next page."
  21. print app.call_method('/')
  22. print app.call_method('/next_page')

因为这里是带参数的装饰器,所以比之前的要多一层嵌套,最外层的接受参数,其次层的接受函数参数。这里知识把url和对应的回调函数记录在url_map中,所以原样返回原函数就好。就不用定义第三个需要返回的函数了

文章来源: blog.csdn.net,作者:网奇,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/jacke121/article/details/77937465

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。