Python打卡第29天

@浙大疏锦行

复习日

知识点回顾

  1. 类的装饰器
  2. 装饰器思想的进一步理解:外部修改、动态
  3. 类方法的定义:内部定义和外部定义

既然函数可以复用,有了类和装饰器,那么类还能进一步封装么?可以的,类也有装饰器

回顾一下,函数的装饰器是 :接收一个函数,返回一个修改后的函数。我们之前是用复用的思想来看装饰器的,换一个角度理解,当你想修改一个函数的时候,可以通过装饰器方法来修改而无需重新定义这个函数。

类也有修饰器,他的逻辑类似:接收一个类,返回一个修改后的类。例如
1. 添加新的方法或属性(如示例中的 log 方法)。
2. 修改原有方法(如替换 __init__ 方法,添加日志)。
3. 甚至可以返回一个全新的类(继承或组合原类)。

1. 类的装饰器

类的装饰器是 Python 中一种强大的元编程工具,它允许你在类定义时或定义后修改类的行为。

基本语法

def decorator(cls):
    # 修改或扩展类的功能
    return cls

@decorator
class MyClass:
    pass

实际示例

def add_methods(cls):
    def new_method(self):
        return "This is a dynamically added method"
    
    cls.new_method = new_method
    cls.class_attribute = "Dynamic attribute"
    return cls

@add_methods
class MyClass:
    def original_method(self):
        return "Original method"

obj = MyClass()
print(obj.original_method())  # 输出: Original method
print(obj.new_method())      # 输出: This is a dynamically added method
print(obj.class_attribute)   # 输出: Dynamic attribute

2. 装饰器思想的深入理解

装饰器的核心思想是"外部修改"和"动态":

外部修改

  • 不侵入原始代码

  • 在不修改源代码的情况下添加功能

  • 符合开放封闭原则(对扩展开放,对修改封闭)

动态特性

  • 运行时而非编译时应用

  • 可以基于条件动态决定如何装饰

  • 支持多层装饰和组合

高级示例:带参数的类装饰器

def add_attributes(**kwargs):
    def decorator(cls):
        for key, value in kwargs.items():
            setattr(cls, key, value)
        return cls
    return decorator

@add_attributes(version="1.0", author="John Doe")
class Document:
    pass

print(Document.version)  # 输出: 1.0
print(Document.author)   # 输出: John Doe
注:
setattr(object, attribute_name, value)
  • 功能:等价于 object.attribute_name = value,但以字符串形式指定属性名

  • 参数

    • object:要操作的对象(类/实例等)

    • attribute_name:字符串形式的属性名

    • value:要设置的属性值(可以是任何类型)

装饰器 vs 继承的核心区别

特性 装饰器 父类继承
目的 动态修改类/函数的行为 建立类之间的层次关系,实现代码复用
时间 运行时生效 类定义时生效
耦合度 低耦合(外部修改) 高耦合(明确父子关系)
语法 @decorator class Child(Parent)

通俗一点 

装饰器 继承
关系 给类"装插件" 创建"父子血缘关系"
时间 运行时随时加 写代码时就要确定
影响 只影响被装饰的类 影响所有子类
比喻 给汽车加装导航/倒车雷达 生产新型号的汽车
代码 @装饰器 class 子类(父类)

3. 类方法的定义

内部定义(标准方式)

class MyClass:
    def instance_method(self):
        return "Instance method"
    
    @classmethod
    def class_method(cls):
        return "Class method"
    
    @staticmethod
    def static_method():
        return "Static method"

外部定义(动态添加)

class MyClass:
    pass

# 添加实例方法
def instance_method(self):
    return "Dynamically added instance method"
MyClass.instance_method = instance_method

# 添加类方法
@classmethod
def class_method(cls):
    return "Dynamically added class method"
MyClass.class_method = class_method

# 添加静态方法
@staticmethod
def static_method():
    return "Dynamically added static method"
MyClass.static_method = static_method

obj = MyClass()
print(obj.instance_method())  # 输出: Dynamically added instance method
print(MyClass.class_method()) # 输出: Dynamically added class method
print(MyClass.static_method()) # 输出: Dynamically added static method

 

方法类型比较

方法类型 装饰器 第一个参数 调用方式 访问权限
实例方法 self obj.method() 可访问实例和类属性
类方法 @classmethod cls Class.method()或obj.method() 只能访问类属性
静态方法 @staticmethod Class.method()或obj.method() 不能访问实例或类属性(独立)

通过类装饰器,可以在不修改类内部代码的情况下,为多个类统一添加功能(如日志、统计)

Python打卡第29天_第1张图片

# 定义类装饰器:为类添加日志功能
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("这是装饰器添加的日志方法")  # 调用装饰器新增的方法
[LOG] 实例化对象: SimplePrinter
Alice: Hello, World!
[LOG] 这是装饰器添加的日志方法

为什么需要保存原始 __init__

  1. 避免无限递归:如果直接在新的 __init__ 中调用 cls.__init__,会导致循环调用

  2. 保持原始行为:确保不破坏类原本的初始化逻辑

  3. 灵活扩展:可以在原始初始化前后插入自定义逻辑

注意到其中的cls.log = log_message 这行代码,他把外部的函数赋值给了类的新定义的属性,这里我们介绍这种写法

实际上,定义类的方法,有2类写法
1. 在类定义内部直接写方法,这是静态方法,一般定义类都这么完成。
2. 在类定义外部定义方法,然后把方法赋值给类的属性---这是一种动态方法,常在装饰器中使用,可以再外部修改类的方法。

Python打卡第29天_第2张图片


两种方式的本质都是将函数对象绑定到类的属性上,只是语法和应用场景不同。装饰器中常用外部赋值,是为了在不修改原类代码的情况下增强类的功能。

ps:之前无论是函数还是类的装饰器,我们都发现是先有装饰器,再有类。那既然我们说了装饰器除了让原本的代码更加清晰可读可复用,还具有修改函数or类的功能。那如何修改之前已经写好的类or函数呢?

所以你还是需要理解 装饰器本质就是一个语法糖,对类而言:@decorator 语法只是 MyClass = decorator(MyClass) 的简写,即使类已定义,仍可手动调用装饰器函数修改它。

总结:装饰器的核心是动态修改类 / 函数,而不改变原代码。通过外部赋值,可以在不修改类定义的前提下,为类添加新方法或修改已有方法。---理解动态的含义
 

你可能感兴趣的:(Python打卡60天,python,开发语言,机器学习)