python装饰器

日常开发中,反复为多个函数手动添加日志记录或者别的附加功能是一项常见痛点。装饰器能有效解决这个问题,只需定义一个装饰器,即可自动化为所有被装饰的函数添加附加功能,无需在每个函数内重复编写代码。装饰器具体是怎么样使用的呢?我们一起来深入探究一下Python装饰器:

目录

一、深入认识装饰器

1、装饰器基础概念:

2、实现原理:

3、实现效果:

4、适用场景:

二、处理装饰器的副作用

三、带参数的装饰器

 精简例子

进阶例子

四、类装饰器

精简例子

进阶例子

五、装饰器模版


一、深入认识装饰器

1、装饰器基础概念:

它可以让你在不改变函数或类原始定义的基础上,对它们的功能进行扩展和修改。装饰器本质上是一个Python函数,它能够接收一个函数作为输入,并返回一个新的函数,这个新函数负责在执行原始函数前后添加额外的操作。

生活中的例子可以帮助我们形象地理解Python装饰器的概念:

  • 手机壳与配件: 手机就像一个基础功能完备的函数,当给手机装上手机壳、安装摄像头滤镜应用或防蓝光贴膜时,这些都可以看作装饰器,它们并没有改变手机打电话和上网的基本功能,而是增强了手机的防护能力、拍照效果或视觉体验。
  • 装修工人: 装修工人可以视为装饰器,他们可以对房子(函数)进行内外装修,比如刷漆、安装吊灯或挂画。虽然房子的基本结构没变,但经过装饰后的房子增加了美观度和功能性。
  • 汽车改装: 一辆汽车出厂时具备基本驾驶功能,通过装饰器(改装厂)可以给汽车添加尾翼、升级音响系统、加强悬挂等,而不改变车辆原有的行驶功能,这些装饰品或改动增强了汽车的性能或舒适性。

装饰器就如同这些生活中的例子一样,它是在不改变原始函数核心功能的基础上,对其添加额外的功能层,实现功能的增强和扩展。

2、实现原理:

装饰器的实现原理基于Python的@语法糖和函数闭包特性。当我们在函数定义前使用@decorator时,Python编译器实际上做了如下转换:

@decorator
def original_function(args):
    # 原始函数体

以上代码等同于:

def original_function(args):
    # 原始函数体
original_function = decorator(original_function)

我们在目标函数前使用@decorator语法时,Python解释器会在编译阶段调用装饰器函数并将目标函数作为参数传入,然后将装饰器函数返回的新函数赋值给原来的目标函数名。在decorator内部定义的闭包(即新函数)通常会调用原始函数,并在其前后执行额外的逻辑。

3、实现效果:

举个栗子:想象你正在经营一家奶茶店,你每天都会做一批新鲜的奶茶。起初,奶茶师傅只负责加入烤奶,但现在你决定在每杯奶茶上添加一层奶盖,使其口感更好。

没有装饰器的实现: 你可能需要修改奶茶师傅的制作流程,在加完奶茶后立即添加加奶盖的动作。

def milk_tea():
    milk_tea = '奶茶'
    apply_Milk_caps(milk_tea)
    return milk_tea

def apply_Milk_caps(milk_tea):
    milk_tea += ' + 奶盖'
    return milk_tea

milk_tea1 = milk_tea()
print(milk_tea1) # 奶茶
milk_tea_caps=apply_Milk_caps(milk_tea1)
print(milk_tea_caps) #奶茶 + 奶盖

使用装饰器的实现: 有了装饰器,我们就可以创建一个“加奶盖装饰器”,它相当于一个中间环节,自动在加入烤奶前后执行额外操作。

def milk_tea_with_caps(milk_tea):
    def wrapper():
        Milk_tea=milk_tea() # 这里是被装饰函数体,在这个例子里,相当于先让奶茶师傅制作奶茶
        Milk_tea += '+ 奶盖' # 这里是装饰器函数体,在这个例子里,相当于在加完奶茶后立即添加加奶盖的动作。
        return Milk_tea
    return wrapper

@milk_tea_with_caps # 使用装饰器,相当于改造了制作奶茶的流程。
def milk_tea():
    milk_tea = '奶茶'
    return milk_tea

caps_milk_tea=milk_tea()#现在调用这个函数,会自动"+ 奶盖"。
print(caps_milk_tea) # 输出:奶茶 + 奶盖

闭包的作用: 装饰器返回的wrapper函数就是一个闭包,它捕获了外部函数(milk_tea_with_caps)的局部变量milk_tea,即使milk_tea_with_caps函数已经执行完毕,wrapper函数仍然可以访问到milk_tea,也就是原始函数的引用。这样,在执行wrapper函数时,便可以在调用milk_tea的同时执行装饰器添加的额外逻辑。

执行过程: 当调用milk_tea时,实际上是在调用wrapper函数。wrapper函数首先执行装饰器预设的逻辑,接着调用原始函数,最后执行装饰器的后置逻辑。

4、适用场景:

在编程过程中,以下几种场景可能会显得代码繁琐或难以维护,而使用装饰器可以有效地解决这些问题:

  • 重复代码过多:当需要为多个函数添加相同或类似的附加功能(如日志记录、性能监控、权限验证等)时,如果不使用装饰器,往往需要在每个函数内部复制粘贴这部分代码,违反了DRY(Don't Repeat Yourself)原则。
  • 功能侵入性强:某些辅助功能(如缓存结果、异常处理)可能会影响函数的主要业务逻辑,使得代码变得混杂不清。装饰器可以将这类附加功能封装在外部,使主业务逻辑保持简洁和专注。
  • 后期修改困难:若在项目的不同阶段,需要对大量函数添加、移除或修改某种共同的行为,直接修改函数源码可能导致牵一发而动全身,风险较大。装饰器则可以通过统一的应用和移除,轻松应对这种需求变更。
  • 横切关注点处理:在复杂的系统中,常常存在跨越多个业务模块的通用需求(如事务控制、权限管理)。装饰器可用于集中处理这些横切关注点,使得各个模块间的耦合度降低,提升系统的可维护性和扩展性。

简而言之,装饰器在遇到需要在多个函数上统一添加、管理和调整附加功能,以及处理横切关注点时,如:统一处理异常、性能检测、资源管理等,有助于提高代码的复用性和整洁度。

二、处理装饰器的副作用

装饰器的一个潜在问题是,当装饰器对原始函数进行包装时,可能会失去原始函数的一些元信息(如函数名、文档字符串等)。为了解决这个问题,Python提供了一个functools.wraps装饰器工厂函数,用于将装饰器内部创建的新函数的元信息与原始函数同步,从而避免装饰器带来的副作用。我们用之前的做奶盖奶茶例子来示范一下:

def milk_tea_with_caps(milk_tea):
    def wrapper():
        Milk_tea=milk_tea() # 这里是被装饰函数体,在这个例子里,相当于先让奶茶师傅制作奶茶
        Milk_tea += '+ 奶盖' # 这里是装饰器函数体,在这个例子里,相当于在加完奶茶后立即添加加奶盖的动作。
        return Milk_tea
    return wrapper

@milk_tea_with_caps # 使用装饰器,相当于改造了制作奶茶的流程。
def milk_tea():
    '''制作奶茶'''
    milk_tea = '奶茶'
    return milk_tea

caps_milk_tea=milk_tea()#现在调用这个函数,会自动"+ 奶盖"。
print(caps_milk_tea) # 输出:奶茶 + 奶盖

print(milk_tea.__name__) #.__name__获取函数名字 输出:wrapper
print(milk_tea.__doc__) #.__doc__获取函数内的注释 输出:None

可以发现要的是milk_tea函数的名字,但反馈的是wrapper函数名,这是为什么呢?是装饰器工作原理, 直接就把milk_tea函数装饰成了wrapper函数,那如果要自己函数的名字, 而不要装饰后的函数名,这样就可以让我们的函数装饰的更好,那应该怎么办?往下看使用functools模块:

import functools
def milk_tea_with_caps(milk_tea):
    @functools.wraps(milk_tea)
    def wrapper():
        Milk_tea=milk_tea() # 这里是被装饰函数体,在这个例子里,相当于先让奶茶师傅制作奶茶
        Milk_tea += '+ 奶盖' # 这里是装饰器函数体,在这个例子里,相当于在加完奶茶后立即添加加奶盖的动作。
        return Milk_tea
    return wrapper

@milk_tea_with_caps # 使用装饰器,相当于改造了制作奶茶的流程。
def milk_tea():
    '''制作奶茶'''
    milk_tea = '奶茶'
    return milk_tea

caps_milk_tea=milk_tea()#现在调用这个函数,会自动"+ 奶盖"。
print(caps_milk_tea) # 输出:奶茶 + 奶盖

print(milk_tea.__name__) #.__name__获取函数名字 输出:milk_tea
print(milk_tea.__doc__) #.__doc__获取函数内的注释 输出:制作奶茶

functools.wraps(milk_tea)将装饰器内部的wrapper函数的__name____module____doc__等属性设置为原始函数milk_tea的相应值,从而解决了装饰器的副作用问题。

三、带参数的装饰器

带参数的装饰器实质上是一种更高阶的装饰器设计,它允许你在定义装饰器时指定一些参数,从而使装饰器具备更大的灵活性和适应性。如果函数内有参数而且每个函数的参数数量不同,也可以给装饰器设置动态参数解决,下面详细阐述带参数装饰器的实现和使用方法:

 精简例子

# 定义一个带参数的装饰器,该装饰器用于给函数调用加上前缀和后缀
def prefix_suffix_decorator(Customize,prefix="", suffix=""):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print(f'自定义随便传的参数>>>>{Customize}')
            print(f"{prefix} 开始执行 {func.__name__}")
            result = func(*args, **kwargs)
            print(f"{suffix} 结束执行 {func.__name__}")
            return result
        return wrapper
    return decorator

# 原始函数
def say_hello(name):
    print(f"Hello, {name}!")

# 使用带参数的装饰器
@prefix_suffix_decorator('洒洒水啦',prefix="[Start]", suffix="[End]")
def decorated_say_hello(name):
    return say_hello(name)

# 调用装饰后的函数
decorated_say_hello("World")

# 输出:
'''
自定义随便传的参数>>>>洒洒水啦
[Start] 开始执行 decorated_say_hello
Hello, World!
[End] 结束执行 decorated_say_hello
'''

在这个例子中,prefix_suffix_decorator是一个带参数的装饰器工厂函数,它可以接收Customize、prefixsuffix三个参数。当我们使用@prefix_suffix_decorator('洒洒水啦',prefix="[Start]", suffix="[End]"装饰say_hello函数时,实际上创建了一个新函数decorated_say_hello,在调用这个新函数时,会在原函数调用前后打印指定的前缀和后缀,实现了在不影响原函数功能的前提下添加额外行为的效果。通过这种方式,我们可根据需要动态地更改前缀和后缀内容,增强了装饰器的灵活性。

可以发现带参数的装饰器就是比普通装饰器多了一个函数嵌套,最外层函数用来接收要传的参数,中间的函数用来接收要装饰的函数,最内层函数接收动态参数且为被装饰函数(原函数) 添加功能。

进阶例子

假设我们正在开发一个简单的日志系统,我们需要为函数添加日志记录功能,记录函数调用时的输入参数、执行时间以及返回结果。我们可以使用带参数的装饰器来实现这个功能,参数用来指定日志级别。

import functools
import time
import logging
# 创建一个带参数的日志装饰器
def log_decorator(log_level=logging.DEBUG):
    def decorator(func):
        functools.wraps(func)
        def wrapper(*args, **kwargs):
            start_time = time.time()
            # 记录函数调用前的日志
            logging.log(log_level, f"调用函数 '{func.__name__}' 带位置参数: {args}, 关键字参数: {kwargs}")
            # 调用原始函数并捕获返回值
            result = func(*args, **kwargs)
            # 记录函数调用后的日志,包括执行时间和返回结果
            end_time = time.time()
            spend_time = end_time - start_time
            logging.log(log_level,
                        f"函数 '{func.__name__}' 完成执行耗时 {spend_time:.6f} seconds. 返回值: {result}")
            return result
        return wrapper
    return decorator

#创建一个装饰器能让原函数结果根据设置参数,返回乘以参数的结果
def my_decorator(num:int):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            result=func(*args,**kwargs)
            print(f"装饰器被调用,原函数结果*{num},结果为:{result*num}")
            return result*num
        return wrapper
    return decorator

接下来调用两个装饰器:

# 使用装饰器
# 配置日志输出
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s: %(message)s")

@log_decorator(logging.INFO)
@my_decorator(3)
def calculate_square(number,say='hello'):
    print(f"{say},正在计算{number}的平方,结果为:{number ** 2}")
    return number ** 2

# 调用函数
result_square = calculate_square(5,say='大家好呀,我们来玩玩带参数装饰器')

'''控制台输出示例(假设日志级别为INFO)
2024-03-15 18:57:03,479 INFO: 调用函数 'calculate_square' 带位置参数: (5,), 关键字参数: {'say': '大家好呀,我们来玩玩带参数装饰器'}
2024-03-15 18:57:03,479 INFO: 函数 'calculate_square' 完成执行耗时 0.000000 seconds. 返回值: 75
大家好呀,我们来玩玩带参数装饰器,正在计算5的平方,结果为:25
装饰器被调用,原函数结果*3,结果为:75
'''

使用两个装饰器还有另外一种写法,叠加使用装饰器时,可以考虑多装饰器的链式使用方式:

# 配置日志输出
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s: %(message)s")

result_square1=log_decorator(logging.INFO)(my_decorator(4)(calculate_square))
result_square1(6,say='大家好呀,我们来玩玩带参数装饰器')

'''控制台输出示例(假设日志级别为INFO)
2024-03-15 18:58:30,384 INFO: 调用函数 'calculate_square' 带位置参数: (6,), 关键字参数: {'say': '大家好呀,我们来玩玩带参数装饰器'}
2024-03-15 18:58:30,385 INFO: 函数 'calculate_square' 完成执行耗时 0.000998 seconds. 返回值: 144
大家好呀,我们来玩玩带参数装饰器,正在计算6的平方等于36
装饰器被调用,原函数结果*4,结果为:144
'''

在这个例子中,log_decorator是一个带参数的装饰器,参数log_level用于指定日志级别。当它装饰函数calculate_square时,会在函数调用前后分别记录一条日志,同时计算并记录函数执行的时间。这样,我们既不影响原始函数的功能,又添加了日志记录功能,而且可以灵活地为不同的函数设置不同的日志级别。my_decorator也是一个带参数的装饰器,参数num用于指定乘以多少倍,当它装饰函数calculate_square时,会在函数调用后,将结果乘以num倍。

四、类装饰器

类装饰器相比函数装饰器具有更高的灵活性和封装性。类装饰器通常包含多个方法和属性,可以通过类的实例状态来保存和共享数据,这使得装饰器能更复杂地控制和扩展被装饰对象的行为。

类装饰器的实现原理主要基于Python的可调用对象(Callable Objects)特性,尤其是__call__特殊方法。类装饰器本质上是一个类,当该类的对象被调用时,它的__call__方法会被执行。通过重写__call__方法,我们可以定义当类实例像函数那样被调用时应执行的逻辑。

类装饰器的基本工作流程如下:

1、当一个类被类装饰器装饰时,装饰器类的__call__方法会被调用,传入被装饰的类作为参数。
2、在__call__方法内部,通常会对传入的类进行某种形式的修改或增强,如添加新的方法、属性,或者改变原有的方法行为等。
3、最后,__call__方法返回一个修改过的类或者该类的实例(取决于装饰器的设计目标)。

一下是一个简单的类装饰器实现原理示例:

class ClassDecorator:
    def __init__(self, klass):
        self.klass = klass  # 保存被装饰的类

    def __call__(self, *args, **kwargs):
        # 在这里可以对类的实例化过程进行干预
        instance = self.klass(*args, **kwargs)  # 创建被装饰类的实例
        # 可以对实例进行额外的修改或添加功能
        instance.new_attribute = "Added by decorator"
        # 或者修改类的方法
        instance.old_method = instance.original_method
        instance.original_method = self.wrap_method(instance.original_method)
        return instance

    def wrap_method(self, original_method):
        def wrapper(*args, **kwargs):
            print("Method is being called with decorator logic")
            return original_method(*args, **kwargs)
        return wrapper

# 使用装饰器
@classDecorator
class MyClass:
    def __init__(self, name):
        self.name = name

    def original_method(self):
        print(f"Hello from {self.name}")

# 创建装饰后的类的实例
my_instance = MyClass("Decorated Class")

# 调用方法时,会触发装饰器内的逻辑
my_instance.original_method()
print(my_instance.new_attribute)

在这个例子中,当我们使用classDecorator装饰MyClass时,实际上是调用了classDecorator类的__call__方法,并将MyClass类传递给了它。装饰器可以在__call__方法中修改类或类的实例,并最终返回这个修改过的实例。classDecorator装饰MyClass时,MyClass的实例化过程就会通过ClassDecorator__call__方法来进行。在__call__方法中,我们创建了MyClass的实例,并对其进行了一些修改,比如添加了一个新属性new_attribute,以及包装了original_method方法,使其在调用时会执行装饰器提供的额外逻辑。

精简例子

我们用一个调用前后的装饰器,精简例子来继续演示:

class CounterDecorator:
    def __init__(self, func):
        self.func = func  # 将被装饰的函数作为实例属性存储
        self.call_count = 0  # 初始化调用计数器为0

    def __call__(self, *args, **kwargs):  # 重载__call__方法,使得实例可以像函数一样被调用
        print(f"调用前(第 {self.call_count} 次)")  # 打印当前调用次数
        result = self.func(*args, **kwargs)  # 调用原始函数,并将传入的参数传递给它
        self.call_count += 1  # 每次调用时,调用计数器加1
        print(f"调用后(第 {self.call_count} 次)")  
        # 再次打印当前调用次数,表示函数调用已经完成
        return result  # 返回原始函数执行的结果

# 原始函数
def say_hello(name):
    print(f"Hello, {name}!")

# 使用类装饰器
@CounterDecorator
def decorated_say_hello(name):
    return say_hello(name)
#等价于
#def decorated_say_hello(name):
#    return CounterDecorator(say_hello)(name)
'''
这意味着当调用decorated_say_hello函数时,实际上是调用了CounterDecorator类的一个实例,
并传入了say_hello函数。由于CounterDecorator类实现了__call__方法,
因此在调用这个实例时,不仅会执行原始的say_hello函数,还会在调用前后打印出调用次数并更新计数器。
'''


# 调用装饰后的函数
decorated_say_hello("Alice")
decorated_say_hello("Bob")
'''
最后,当我们分别调用decorated_say_hello("Alice")和decorated_say_hello("Bob")时:
第一次调用时,输出“调用前(第 O 次)”,执行say_hello("Alice"),输出“Hello, Alice!”,
然后输出“调用后(第 1 次)”。
第二次调用时,输出“调用前(第 1 次)”,执行say_hello("Bob"),输出“Hello, Bob!”,
然后输出“调用后(第 2 次)”。
这样,通过装饰器的方式,我们既保留了原始函数的功能,又增加了额外的计数和日志功能。
'''

# 输出:
'''
调用前(第 0 次)
Hello, Alice!
调用后(第 1 次)
调用前(第 1 次)
Hello, Bob!
调用后(第 2 次)
'''

在这个例子中,CounterDecorator类装饰器在每次调用decorated_say_hello函数时,都会通过__call__方法跟踪并打印调用次数。同时,由于装饰器类内部有call_count属性,所以它可以持久地存储并更新函数调用次数的信息,这是单纯使用函数装饰器难以做到的。类装饰器通过这种特性,提供了更丰富的功能以及更好的封装和内聚性。

进阶例子

类装饰器的进阶用法通常涉及更复杂的功能,例如状态管理、上下文相关的操作、或者动态修改类行为等。以下是一个使用类装饰器实现接口认证和统计请求次数的例子:

import time
from collections import defaultdict
import functools

# 定义认证异常类
class AuthenticationError(Exception):
    """自定义认证失败异常"""
# 定义认证类
class Authenticator:
    def __init__(self):
        self.tokens = {
            "valid_token": {"user_id": '***', "permissions": ["***", "***"]},
            # 示例数据,真实情况下可能从数据库或其他认证源获取
        }
    def authenticate(self, token):
        """
        根据给定的token进行认证,如果token有效则返回True,
        否则返回False并抛出身份验证错误AuthenticationError
        """
        if token in self.tokens:
            return True
        else:
            raise AuthenticationError(f"无效的token: {token}")

# 定义类装饰器,用于统计请求次数和处理认证
class RequestStatisticsDecorator:#请求统计信息装饰器
    def __init__(self, service_class):
        self.service_class = service_class
        self.request_counts = defaultdict(int) # 用于记录请求次数的字典, defaultdict(int)不论请求是否已存在,都可以直接增加计数
        self.authenticator = Authenticator()  # 创建Authenticator实例

    def __call__(self, *args, **kwargs):
        # 在创建类实例时调用
        instance = self.service_class(*args, **kwargs)
        # 为类实例的方法添加认证和计数逻辑
        for attr_name in ('process_request', '__call__'):
            original_method = getattr(instance, attr_name, None)
            if callable(original_method):
                wrapped_method = self.authenticate_and_count(original_method) # 调用authenticate_and_count方法
                setattr(instance, attr_name, wrapped_method)
        # 添加一个属性到服务类实例中,用于存储并访问装饰器实例
        instance.get_request_counts = lambda: self.request_counts
        return instance

    def authenticate_and_count(self, method): # 定义 身份验证和计数 方法
        @functools.wraps(method)
        def wrapper(*args, **kwargs):
            # 先进行认证
            token = kwargs.get('token')
            try:
                self.authenticator.authenticate(token)
            except AuthenticationError as e:
                # 记录失败请求
                self.request_counts["failed"] += 1
                raise e

            # 记录请求开始时间
            start_time = time.time()

            try:
                result = method(*args, **kwargs)
            except Exception as e:
                # 若执行过程中发生其他异常,仍记录为失败请求
                self.request_counts["failed"] += 1
                raise e
            else:
                # 记录成功请求及其执行时间
                self.request_counts["success"] += 1
                elapsed_time = time.time() - start_time
                self.request_counts[f'success_{elapsed_time:.2f}_seconds'] += 1

            return result

        return wrapper

# 定义基础服务类
@RequestStatisticsDecorator
class AuthenticationService:
    def __init__(self):
        pass

    def process_request(self, token, data):
        # 处理请求的逻辑
        print(f"使用token: {token} 和数据: {data} 进行请求")
        return "请求已处理"


# 创建增强服务类的实例并尝试调用方法
service = AuthenticationService()
# 使用有效token调用
try:
    response = service.process_request(token="valid_token", data={"user": "***"})
    print(response)
except AuthenticationError as e:
    print(e)

'''控制台输出:
使用token: valid_token 和数据: {'user': '***'} 进行请求
请求已处理
'''
# 使用无效token调用
try:
    service.process_request(token="invalid_token", data={"user": "***"})
except AuthenticationError as e:
    print(e)

'''控制台输出:
无效的token: invalid_token
'''

# 获取并打印请求统计信息
# 创建服务类的实例并进行请求处理
service_instance = AuthenticationService()
service_instance.process_request(token="valid_token", data={"some": "data"})
service_instance.process_request(token="valid_token", data={"some": "data"})
'''
使用token: valid_token 和数据: {'some': 'data'} 进行请求
使用token: valid_token 和数据: {'some': 'data'} 进行请求
'''
print(service_instance.get_request_counts())
# 获取装饰器实例并访问计数统计信息
'''
defaultdict(<class 'int'>, {'success': 3, 'success_0.00_seconds': 3, 'failed': 1})
'''

在这个例子中,RequestStatisticsDecorator是一个类装饰器,它在装饰AuthenticationService时不仅添加了认证功能,还实现了请求计数和请求耗时分类统计的功能。通过在authenticate_and_count方法中使用闭包,我们能够在原始类的方法调用前后插入认证逻辑和计数逻辑。这样,每次调用被装饰类的特定方法时,都会执行认证、计数等附加行为,同时不会影响原始方法的主体逻辑。

总结一下类装饰器的优势:

  • 模块化:类装饰器有助于将特定的功能(如认证、日志记录、性能检测等)模块化,提高了代码的复用性。
  • 非侵入性:装饰器不会更改原始类的代码,增强了程序的整洁性和可维护性。
  • 灵活性:通过类装饰器可以根据需要随时开启或关闭附加功能,适应不同的运行环境或需求变化。
  • 结构清晰:将跨多个类或方法共有的逻辑抽象出来,放到装饰器中集中处理,使得主业务代码更为专注和简洁。
  • 可扩展性:可以很容易地为新的类添加装饰器功能,或修改现有装饰器以适应新的需求。

五、装饰器模版

函数装饰器:

import functools

def my_decorator(func):
    @functools.wraps(func)  # 保留原函数的元信息
    def wrapper(*args, **kwargs):
        # 在调用原函数前的操作
        before_result = "调用函数前执行的操作"
        
        result = func(*args, **kwargs)  # 调用原函数
        
        # 在调用原函数后的工作
        after_result = "调用函数后执行的操作"
        
        # 返回处理过的结果,也可以直接返回原函数的结果
        return before_result, result, after_result
    return wrapper

@my_decorator
def target_function(param1, param2):
    print("Inside target function")
    return param1 + param2

# 调用装饰器修饰过的函数
result = target_function(1, 2)

类装饰器:

class MyClassDecorator:
    def __init__(self, cls):
        self.cls = cls

    def __call__(self, *args, **kwargs):
        # 在创建类实例前的操作
        before_instance = "Before creating class instance"

        # 创建并返回类实例,可以在这里添加额外属性或方法
        instance = self.cls(*args, **kwargs)
        instance.additional_attribute = "Added by decorator"

        # 在创建类实例后的工作
        after_instance = "After creating class instance"

        # 返回装饰过的类实例
        return instance, before_instance, after_instance

@MyClassDecorator
class TargetClass:
    def __init__(self, param1, param2):
        self.param1 = param1
        self.param2 = param2

    def display_info(self):
        print(f"Inside class method, param1: {self.param1}, param2: {self.param2}")

# 创建装饰器修饰过的类的实例
decorated_instance, before, after = TargetClass(1, 2)
decorated_instance.display_info()

爆肝两天,收集整合了资料笔记,写了这篇长文~ 学无止境,希望累到的同时,这篇文章能帮助大家搞定装饰器。

  • 33
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值