讲装饰器之前我们先来了解一下****开放封闭原则(面向对象原则的核心**)**
开放封闭原则:软件实体应该是可扩展,而不可修改的。也就是说,对扩展是开放的,而对修改是封闭的。
装饰器的作用:在不更改原功能函数内部代码,并且不改变调用方法的情况下为原代码添加新的功能。
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
欢迎来到testingpai.com!
注册 关于