Python装饰器及多装饰器执行顺序

发布时间:2023-11-18 16:00

Python中的装饰器简言之就是一个函数,一个可以让其他函数在不改变自身代码的前提下实现额外功能(如添加日志、进行权限校验等)的函数。使用装饰器能够提升代码的复用性,增强代码的可读性。

Python中实现装饰器的基本方式是先定义一个外壳,再实现待装饰的对象,如下例:

def decorator(func):
    """ 定义一个名为decorator的装饰器, func即被装饰函数代表的形参"""
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

@decorator  
def func1(*args, **kwargs):
    """ func1被装饰的函数 """
    pass

# 此处的@decorator为装饰器执行的核心,它等价于 func1 = decorator(func1)
# @xxx 必须放在被装饰函数之前
# 将原函数(func1)作为参数传递给decorator将其发返回值赋值(wrapper函数)给func1,也就是说原函数
# func经过装饰过后指向了一个新的函数wrapper,当我们之后再调用func1的时候实际是调用一个实现原函数# 基础功能之外另外功能的函数

Python中装饰器概括来说可以分为四类(从装饰器与装饰对象来说):函数装饰函数,函数装饰类,类装饰类,类装饰函数

一、函数装饰函数(即装饰器与被装饰对象都是函数)

 装饰器函数本身可以分为带参数与不带参数的

(1)不带参数的函数装饰器

import time
def decorator(func):
    """实现一个普通装饰器,打印每次函数开始执行的时间"""
    def wrapper(*args, **kwargs):
        excu_time = time.strftime('%Y-%m-%d', time.localtime(time.time())) 
        print(f"当前函数 {func.__name__} 的执行时间是:{excu_time}")
        return func(*args, **kwargs)
    return wrapper

@decorator   # 等价于 func2 = decorator(func2)
def func2():
    print("python 函数装饰器")

func2()

>> 当前函数 func2 的执行时间是:2022-07-10

(2)带参数的函数装饰器(即装饰器函数本身带参数)

# 如果装饰器函数本身带参数,那么我们需要在原装饰器之上再定义一层,用来接收装饰器本身的参数
def decorator(para):
    """ 实现一个带参数的装饰器,在函数每次执行之前打印一句话 """
    print(f'hello {para}')
    def wrapper(func):
        def inner(*args, **kwargs):
            return func(*args, **kwargs)
        return inner
    return wrapper

@decorator('python')  # 等价于 func3 = decorator('python')(func3),调用时需要传入参数
def func3():
    print("python !!!")

func3()
>> hello python
>> python !!!

二、函数装饰类(装饰器本身是一个函数,被装饰对象是一个类)

函数装饰类,可以实现对类添加额外的属性、方法等,同样分为带参数(实现方式同上)与不带参数。

def decorator(cls):
    """实现一个装饰器为类添加一个属性"""
    def wrapper():
        cls.new_attr = "python decorator"
        return cls
    return wrapper

@decorator # 等价于A = decorator(A) 即A = wrapper,是一个函数对象
class A:
    def __init__(self, name):
        self.name = name

# 查看A的属性中是否实现了装饰器,由于前面返回的是wrapper,所以调用时使用A()即该方法返回的cls
print(hasattr(A(), 'new_attr'))
>> True

三、装饰器类装饰类

装饰器类装饰类,即装饰器与被装饰对象都是类

"""
一个类来装饰另一个类,不同于其他几种装饰器的实现方式,
在使用类装饰器的时候需要注意:
    在装饰器类内需要实现__init__()和__call__()方法
"""
class Decorator:

    """定义一个类装饰器,其初始化方法,需要传入self, decorated_class, *args, **kwargs
    其中decorated_class: 被装饰的类, *args, **kw被装饰类的初始化方法所需要传入的参数
    """
    def __init__(self, decorated_class, *args, **kwargs):
        # 将需要装饰的类作为装饰类的一个实例对象属性
        print("The first step begin")

        self.decorated_class = decorated_class

        print("The first step end")

    def __call__(self, *args, **kwargs):
        # 对被装饰的类执行初始化,并返回创建的实例对象
        print("the second step")
        return self.decorated_class(*args, **kwargs)

"""
此处即 DecoratedClass = Decorator(DecoratedClass), 当我们使用DecoratedClass(*agrs, **kwargs)创建
一个DecoratedClass的实例对象时,将自动调用__call__中实现的原本的DecoratedClass的实例对象初始化并返回该实例对象
"""
@Decorator  
class DecoratedClass:
    def __init__(self, action, des):
        self.action = action
        self.des = des

    def getele(self):
        return self.des

obj = DecoratedClass("被装饰的类", "python的类装饰器")
print(obj.getele())

>> The first step begin
>> The first step end
>> the second step
>> python的类装饰器

四、 装饰器类装饰函数

装饰器类装饰函数,即装饰器是一个类,被装饰对象是一个函数

"""
函数的类装饰器,即使用一个类的来装饰一个函数,需要注意的是与类的类装饰器同理,为了保证被装饰的函数能够被调用,我们同样需要在装饰类中实现__init()和__call__方法
"""
class Decorate:
    """定义一个装饰器类,实现当我们的函数执行出现异常的时候将报错信息写入到日志文件中"""
    def __init__(self, func, *args, **kwargs):
       """此处的参数func代表需要装饰的函数"""
       self.__func = func

    def __call__(self, *args, **kwargs):
        """实现__call__方法,使被装饰的函数能够被执行"""
        try:
             return self.__func(*args, **kwargs)

        except Exception as e:
             # 获取当前时间的年月日
            day = time.strftime("%Y_%m_%d", time.localtime(time.time()))  
            # 获取当前时间的分秒数据
            tm = time.strftime("%H_%M_%S", time.localtime(time.time()))  

            error_info = f"程序执行异常信息: {day} {tm} : {e}"
            with open("./text", 'w+') as f:
                f.write(error_info)
            raise e

        finally:
            return None

@Decorate  # 此处即 func4 = Decorate(func4) ,当我们调用func4(xx)时将执行__call__中定义的方        
           # 法(即原函数)
def func4(a, b):
    return a / b

print(func4(30, 2))
>> 15.0

print(func4(30, 0))
# 抛出异常,并将异常信息写入到text文件中 
>> 程序执行异常信息: 2022_07_10 15_03_42 : division by zero

五、多装饰器的执行顺序 

在Python中有时会遇到多个装饰器同时装饰一个函数的场景,其装饰顺序与执行顺序为:靠近原函数的先进行装饰后执行,离原函数远的后装饰先执行。如下例:

import datetime
def decorator1(func):
    def wrapper1(*args, **kwargs):
        print(func.__name__)
        print("这是第一个装饰器, 执行时间为: ", datetime.datetime.now())
        time.sleep(5)
        return func(*args, **kwargs)
    return wrapper1

def decorator2(func):
    def wrapper2(*args, **kwargs):
        print(func.__name__)
        print("这是第二个装饰器, 执行时间为: ", datetime.datetime.now())
        time.sleep(5)
        return func(*args, **kwargs)
    return wrapper2

@decorator1  # 此处即 func5 = decorator1(decorator2(func5))
@decorator2  # 此处即 func5 = decorator2(func5)
def func5():
    print("这是原函数,执行时间为: ", datetime.datetime.now())

func5()

>> wrapper2
>> 这是第一个装饰器, 执行时间为:  2022-07-10 15:26:18.000297

>> func5
>> 这是第二个装饰器, 执行时间为:  2022-07-10 15:26:23.008316

>> 这是原函数,执行时间为:  2022-07-10 15:26:28.015128

# 从上面执行结果可知先被执行的装饰器2,而装饰的顺序则是由上至下的,也即先装饰后执行,后装饰先执# 行

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号