DAY 29 复习日:类的装饰器

发布于:2025-06-15 ⋅ 阅读:(19) ⋅ 点赞:(0)

知识点回顾

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

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

一、类装饰器

类也有修饰器,他的逻辑类似:接收一个类,返回一个修改后的类。例如

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

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

类装饰器 vs 函数装饰器:核心区别

特性 函数装饰器 类装饰器
作用对象 函数(function 类(class
传入参数 接收函数作为参数(def decorator(func): 接收类作为参数(def decorator(cls):
返回值 返回包装后的函数(通常是闭包) 返回修改后的类(可以是原类或新类)
常见用途 修改函数行为(如日志、计时、权限验证) 修改类的结构(如添加属性、方法、修改初始化逻辑)
核心逻辑 用闭包包裹函数,在不修改函数代码的前提下扩展功能 直接修改类的定义(如添加/替换方法、属性)
# 定义类装饰器:为类添加日志功能
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] 这是装饰器添加的日志方法

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

二、定义类的方法

实际上,有2类写法:

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

本质区别

特性 类内部定义方法 外部赋值定义方法
语法 在 class 块内使用 def 定义函数后赋值给类属性(如 cls.fn = fn
作用域 方法可以直接访问类的其他私有成员 需要通过 self 或类名显式访问
动态性 类定义后方法固定 可以在运行时动态添加/修改方法
常见场景 常规类定义 装饰器、元类、动态编程

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

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

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

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

三、附:昨日作业

题目1:定义圆(Circle)类

要求:

1. 包含属性:半径 radius。

2. 包含方法:

  • calculate_area():计算圆的面积(公式:πr²)。
  • calculate_circumference():计算圆的周长(公式:2πr)。

3. 初始化时需传入半径,默认值为 1。

class Circle:
  def __init__(self, radius=1):
    self.radius = radius
  
  def calculate_area(self):
    print(f"圆的面积为:{3.14 * self.radius ** 2:.2f}")

  def calculate_circumference(self):
    print(f"圆的周长为:{2 * 3.14 * self.radius:.2f}")

# 实例化并调用类中的方法
c = Circle(3)
c.calculate_area()
c.calculate_circumference()

输出:

圆的面积为:28.26
圆的周长为:18.84

题目2:定义长方形(Rectangle)类

1. 包含属性:长 length、宽 width。

2. 包含方法:

  • calculate_area():计算面积(公式:长×宽)。
  • calculate_perimeter():计算周长(公式:2×(长+宽))。
  • is_square() 方法,判断是否为正方形(长 == 宽)。

3. 初始化时需传入长和宽,默认值均为 1。

class Rectamgle:
  def __init__(self, lengh=1, width=1):
    self.lengh = lengh
    self.width = width
  
  def calculate_area(self):
    print(f"长方形的面积为:{self.lengh * self.width}")

  def calculate_perimeter(self):
    print(f"长方形的周长为:{(self.lengh + self.width * 2)}")

  def is_square(self):
    if self.lengh == self.width:
      print("该图形是正方形")
    else:
      print("该图形不是正方形")

# 实例化并调用类中的方法
r = Rectamgle(2, 3)
r.calculate_area()
r.calculate_perimeter()
r.is_square() 

输出:

长方形的面积为:6
长方形的周长为:8
该图形不是正方形

题目3:图形工厂

创建一个工厂函数 create_shape(shape_type, *args),根据类型创建不同图形对象:图形工厂(函数或类)

  • shape_type="circle":创建圆(参数:半径)。
  • shape_type="rectangle":创建长方形(参数:长、宽)。
class Circle:
  def __init__(self, radius=1):
    self.radius = radius
  
  def calculate_area(self):
    print(f"圆的面积为:{3.14 * self.radius ** 2:.2f}")

  def calculate_circumference(self):
    print(f"圆的周长为:{2 * 3.14 * self.radius:.2f}\n")


class Rectamgle:
  def __init__(self, lengh=1, width=1):
    self.lengh = lengh
    self.width = width
  
  def calculate_area(self):
    print(f"长方形的面积为:{self.lengh * self.width}")

  def calculate_perimeter(self):
    print(f"长方形的周长为:{(self.lengh + self.width * 2)}")

  def is_square(self):
    if self.lengh == self.width:
      print("该图形是正方形")
    else:
      print("该图形不是正方形")


def create_shape(shape_type, *args):
  if shape_type == "circle":  # shape_type是圆形
    c = Circle(*args)
    c.calculate_area()
    c.calculate_circumference()

  elif shape_type == "rectangle":  # shape_type是长方形
    r = Rectamgle(*args)
    r.calculate_area()
    r.calculate_perimeter()
    r.is_square() 
    
  else:
    print(f"不存在{shape_type}对应的类。")

create_shape("circle", 3)
create_shape("rectangle", 3, 2)

输出:

圆的面积为:28.26
圆的周长为:18.84

长方形的面积为:6
长方形的周长为:7
该图形不是正方形

@ 浙大疏锦行