详解 python 装饰器

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

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

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

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

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 更新了该帖
回帖
请输入回帖内容 ...