DAY 29 复习日:类的装饰器

@浙大疏锦行https://blog.csdn.net/weixin_45655710

作业: 复习类和函数的知识点,写下自己过去29天的学习心得,如对函数和类的理解,对python这门工具的理解等
# 定义类装饰器:为类添加日志功能
def class_logger(cls):
    # 保存原始的 __init__ 方法
    original_init = cls.__init__

    def new_init(self, *args, **kwargs):
        # 新增实例化日志
        print(f"[LOG] 实例化对象: {cls.__name__}")
        original_init(self, *args, **kwargs)  # 调用原始构造方法

    # 将类的 __init__ 方法替换为新方法
    cls.__init__ = new_init

    # 为类添加一个日志方法(示例)
    def log_message(self, message):
        print(f"[LOG] {message}")

    cls.log = log_message  # 将方法绑定到类,这是一种将外部函数添加为类的属性的方法
    return cls


# 定义简单打印类,应用装饰器
# 同样是语法糖的写法
@class_logger
class SimplePrinter:
    def __init__(self, name):
        self.name = name  # 构造方法:初始化名称

    def print_text(self, text):
        """简单打印方法"""
        print(f"{self.name}: {text}")


# 使用示例
printer = SimplePrinter("Alice")  # 实例化时触发装饰器的日志
printer.print_text("Hello, World!")  # 调用普通方法
printer.log("这是装饰器添加的日志方法")  # 调用装饰器新增的方法

代码整体目标

这段代码的核心目标是在不修改一个类(SimplePrinter)内部代码的情况下,给这个类增加新的功能。这就像给一个手机套上一个智能手机壳,手机本身没变,但增加了无线充电和支架功能。

它主要做了两件事:

  1. 每当这个类创建一个实例(对象)时,自动打印一条日志。
  2. 为这个类增加一个全新的方法,叫做 log()

实现这个目标的工具就是 类装饰器 (class_logger)

代码分步解析

我们从上到下,逐段来看代码的作用。

第一部分:定义类装饰器 class_logger

# 定义类装饰器:为类添加日志功能
def class_logger(cls):
    # ... (内部代码) ...
    return cls
  • 这是一个函数,名叫 class_logger。当它被用作装饰器时,它会接收一个参数 cls
  • 这里的 cls 就是被装饰的那个类本身(在我们的例子中,就是 SimplePrinter 类)。
  • 装饰器的最后必须返回一个类,通常是修改后的 cls

现在看装饰器内部做了什么:

    # 保存原始的 __init__ 方法
    original_init = cls.__init__
  • cls.__init__ 指的是 SimplePrinter 类原始的构造方法 def __init__(self, name): ...
  • 我们用一个变量 original_init 把它存起来。这步非常关键,就像装修前先把原来的家具搬出去,免得弄坏,后面还要再搬回来用。我们不想完全破坏掉原来的初始化功能。
    def new_init(self, *args, **kwargs):
        # 新增实例化日志
        print(f"[LOG] 实例化对象: {cls.__name__}")
        original_init(self, *args, **kwargs)  # 调用原始构造方法
  • 这里我们定义了一个全新的函数 new_init。这个函数即将用来替换SimplePrinter 原来的 __init__ 方法。
  • 第1步:添加新功能print(f"[LOG] ...") 是我们想新增的日志打印功能。
  • 第2步:执行原始功能original_init(self, *args, **kwargs) 就是在调用我们刚刚保存好的那个原始 __init__ 方法。这样就保证了 self.name = name 这句代码依然会被执行,对象的核心功能没有丢失。
    • *args, **kwargs 是为了让我们的新方法能接收任意参数,并原封不动地传递给原始方法,增强了通用性。
    # 将类的 __init__ 方法替换为新方法
    cls.__init__ = new_init
  • 偷梁换柱:这行代码是魔法发生的地方。它把 SimplePrinter 类原来的 __init__ 方法,换成了我们刚刚定义的、带有日志功能的新方法 new_init
    # 为类添加一个日志方法(示例)
    def log_message(self, message):
        print(f"[LOG] {message}")

    cls.log = log_message  # 将方法绑定到类
    return cls
  • 添加全新功能:这里我们定义了一个完全独立的新函数 log_message
  • 然后通过 cls.log = log_message,我们给 SimplePrinter动态地增加了一个新的方法,并把它命名为 log
  • 最后,return cls 返回这个被我们“改造”过的 SimplePrinter 类。

第二部分:定义 SimplePrinter 类并应用装饰器

@class_logger
class SimplePrinter:
    def __init__(self, name):
        self.name = name

    def print_text(self, text):
        print(f"{self.name}: {text}")
  • @class_logger 是一个“语法糖”,它是一种简洁的写法,完全等价于在类定义下面写 SimplePrinter = class_logger(SimplePrinter)
  • 它的作用就是:当Python解释器读到 class SimplePrinter: ... 这段代码并创建好 SimplePrinter 类之后,立刻马上把这个类作为参数传递给 class_logger 函数去“加工”。加工完成后,返回的新类重新赋值给 SimplePrinter 这个名字。
  • 所以,我们后续使用的 SimplePrinter 其实已经是被 class_logger 修改过的版本了。

第三部分:使用示例

# 使用示例
printer = SimplePrinter("Alice")
  • 这一行代码在创建 SimplePrinter 的实例。
  • 由于 __init__ 方法已经被替换成了 new_init,所以实际执行的是 new_init("Alice")
  • new_init 内部会先打印 [LOG] 实例化对象: SimplePrinter
  • 然后调用原始的 __init__ 方法,完成 self.name = "Alice" 的操作。
printer.print_text("Hello, World!")
  • print_text 方法在装饰器中没有被修改,所以它还是和原来一样正常工作。输出 Alice: Hello, World!
printer.log("这是装饰器添加的日志方法")
  • log 方法是装饰器 class_logger 动态添加进去的。所以我们可以直接调用它。
  • 它会执行 log_message 函数的逻辑,打印出 [LOG] 这是装饰器添加的日志方法

总结

装饰器 class_logger 就像一个加工厂,SimplePrinter 类是原材料。原材料送进工厂后,工厂(装饰器)对它进行了两项改造:

  1. 修改了原有的 __init__ 方法,在保留原功能的基础上增加了日志打印。
  2. 新增了一个原本没有的 log 方法。

最后出厂的 SimplePrinter 就是一个功能增强版的产品。这就是类装饰器的核心思想和作用。

你可能感兴趣的:(python训练营打卡笔记,python)