详解 python 装饰器

本贴最后更新于 928 天前,其中的信息可能已经时移世易

讲装饰器之前我们先来了解一下****开放封闭原则(面向对象原则的核心**)**

开放封闭原则:软件实体应该是可扩展,而不可修改的。也就是说,对扩展是开放的,而对修改是封闭的。

装饰器的作用:在不更改原功能函数内部代码,并且不改变调用方法的情况下为原代码添加新的功能。

1、普通装饰器

小案例:
def decorator(func): def wrapper():     print("---开机,打开软件--")     func()     print("---关机,底薪到手---") return wrapper @decorator  # 作用 @decorator 等同于 ===> work1 = decorator(work1) def work1(): print("----写代码---") work1()

**装饰器原理阐述:**将被装饰的函数当做一个参数传到装饰器中,并且让被装饰的函数名指向装饰器内部的函数,在装饰器的内部函数再调用被装饰的函数

练习:

需求:实现一个可以统计任意函数执行时间的装饰器!

import time def count_time(func): def wrapper():     strat_time = time.time()  #获取开始时的时间     func()  #运行被装饰的函数     end_time = time.time()   # 获取结束时的时间     t_time = end_time - strat_time  #算出运行总时间     print('运行总时间%:',t_time)    #打印运行总时间 return wrapper @count_time def work(): time.sleep(2) print('原来函数的功能代码')     work()

2、装饰有参数和返回值函数

问题:对于一个有参数和返回值的函数,还能不能使用上面的计时装饰器
def work(a,b): res = a+ b retrun res
装饰器优化(实现传参)
  • 装饰器内部嵌套函数接收参数
  • 调用函数时,传入参数
def count_time(func): def wrapper(a,b):     strat_time = time.time()     func(a,b)     end_time = time.time()       t_time = end_time - strat_time     print('运行总时间%:',t_time)   return wrapper
装饰器优化(返回值)
  • 调用功能函数,接收功能函数返回的结果,装饰器执行完返回结果
def count_time(func): def wrapper(a,b):     strat_time = time.time()     # 调用功能函数,接收功能函数返回的结果     res = func(a,b)     end_time = time.time()       t_time = end_time - strat_time     print('运行总时间%:',t_time)       # 返回结果     return res return wrapper

3、通用装饰器

  • 被装饰的函数,一部分带参数,一部分不带参数怎么处理?
def work1(a,b):    print("------work1-----")    res = a+b    retrun res def work2(a,b,c):    print("------work2-----")    res = a+b+c    retrun res

如果同一个装饰器既要装饰有参数的函数,又要装饰无参数的函数,那么我们在传参的时候就设置成不定长参数,这样不管被装饰的函数有没有参数都能用。

def count_time(func): def wrapper(*args,**kwargs):     strat_time = time.time()     # 调用功能函数,接收功能函数返回的结果     res = func(args,kwargs)     end_time = time.time()       t_time = end_time - strat_time     print('运行总时间%:',t_time)       # 返回结果     return res return wrapper

4、装饰器装饰类

使用类装饰器的时候,记得要返回被装饰的类调用的结果

def decorator(cls): def wrapper(*args, **kwargs):     print("----装饰器扩展代码1------")     # 通过类实例化对象     res = cls()     print("----装饰器扩展代码2------")     return res return wrapper @decorator  # MyClass =decorator(MyClass) class MyClass: pass

5、装饰器传参数

def musen(name, age):  def decoreter(func):     def wrapper(*args, **kwargs):         print("装饰器传递的参数name:", name)         print("装饰器传递的参数age:", age)         func(*args, **kwargs)     return wrapper return decoreter
  • 最外层参数,接收的是装饰器的参数
  • 第二层参数,接收是被装饰的函数
  • 第三层参数,接收的是被装饰函数的参数

6、类实现装饰器(魔术方法中讲)

前面我们是用闭包函数来实现的装饰器,那么接下来给大家扩展一下,使用类来当做一个装饰器来用

  • 如果要把类当做一个装饰器来用,有两步操作,
    • 首先在 __init__方法中,把被装饰的函数赋值给一个实例属性,
    • 然后再了类里面实现一个 __call__方法,在call方法中调用原来的函数。
class Test(object): def __init__(self,func):       self.func = func     def __call__(self, *args, **kwargs):     print('这个是类装饰器')     self.func(*args,**kwargs)

7、装饰器的副作用

问题:函数/类 在被装饰器装饰了之后,会改变原函数名的指向,无法再通过原函数名去获取函数原有的属性,
def decorator1(func):  def wrapper(a, b):     res = func(a, b)     return res return wrapper @decorator1 def work(a, b): """ 实现两个对象相加的方法 :param a: 数字1 :param b: 数字2 :return: 两个数相加的结果 """ res = a + b print('a+b的结果为:', res) # 获取函数的文档字符串注释 print(work.__doc__) # 获取函数名 print(work.__name__)
消除装饰器的副作用:functools.wraps
from functools import wraps def decorator1(func): # 消除装饰器的副作用 @wraps(func) def wrapper(a, b):     print("----装饰器扩展功能代码----")     res = func(a, b)     return res return wrapper
1 操作
mslemonban 在 2022-08-29 13:39:43 更新了该帖
回帖
请输入回帖内容 ...