当前位置: 首页 > news >正文

DAY 29 复习日:类的装饰器-2025.9.16

复习日:类的装饰器

知识点回顾

  1. 类的装饰器
  2. 装饰器思想的进一步理解:外部修改、动态
  3. 类方法的定义:内部定义和外部定义
    在这里插入图片描述
    在这里插入图片描述

作业
复习类和函数的知识点,写下自己过去29天的学习心得,如对函数和类的理解,对python这门工具的理解等,未来再过几个专题部分我们即将开启深度学习部分。

笔记:

1. 类的装饰器

类的装饰器与函数装饰器思想一致,是接收类作为参数并返回新类的工具,用于在不修改类内部代码的前提下,动态扩展或修改类的行为。

类装饰器的常见用途:

  • 为类添加属性或方法
  • 拦截类的实例化过程
  • 对类的属性进行校验等

示例:为类添加通用属性
下面的装饰器会为被装饰的类自动添加versionauthor属性:

def add_meta_info(version, author):# 定义类装饰器,接收版本和作者作为参数def decorator(cls):# 给类添加属性cls.version = versioncls.author = author# 返回修改后的类return clsreturn decorator# 使用类装饰器
@add_meta_info(version="1.0.0", author="Alice")
class MyClass:def __init__(self, name):self.name = name# 测试
obj = MyClass("test")
print(MyClass.version)  # 输出:1.0.0
print(MyClass.author)   # 输出:Alice

2. 装饰器思想的进一步理解:外部修改、动态

装饰器的核心思想是 “开放 - 封闭原则”:对扩展开放(可以新增功能),对修改封闭(不改变原有代码)。

  • 外部修改:装饰器在原有对象(函数 / 类)的 “外部” 进行包装,而非侵入式修改其内部实现。例如,给一个计算函数添加日志功能时,无需修改函数的计算逻辑,只需在外部用装饰器包裹。
  • 动态性:装饰器的作用是在运行时动态生效的,可根据需求灵活添加 / 移除功能。例如,生产环境需要日志装饰器,测试环境可以移除,无需修改原函数。

示例:动态添加日志功能

import functoolsdef add_logging(cls):"""类装饰器:为类的所有方法添加日志功能"""# 遍历类的所有属性for name, method in cls.__dict__.items():# 只处理实例方法、类方法和静态方法(排除特殊方法如__init__可添加判断)if callable(method):# 定义包装函数,添加日志逻辑@functools.wraps(method)  # 保留原方法的元信息def wrapper(*args, **kwargs):# 打印调用日志print(f"[日志] 调用方法: {name}")print(f"[日志] 参数: args={args}, kwargs={kwargs}")# 调用原方法result = method(*args, **kwargs)# 打印返回值日志print(f"[日志] 返回值: {result}\n")return result# 用包装后的方法替换原方法setattr(cls, name, wrapper)return cls# 测试:使用装饰器为类添加日志
@add_logging
class Calculator:def add(self, a, b):return a + bdef multiply(self, x, y):return x * y@classmethoddef subtract(cls, m, n):  # 类方法也会被添加日志return m - n# 实例化并调用方法,观察日志输出
calc = Calculator()
calc.add(2, 3)
calc.multiply(4, 5)
Calculator.subtract(10, 7)

3. 类方法的定义:内部定义和外部定义

实际上,定义类的方法,有2类写法

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

本质区别

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

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

类方法是与类本身绑定的方法,而非与实例绑定,第一个参数通常为cls(代表类本身),需用@classmethod装饰器标识。

(1)内部定义(最常用)

直接在类的内部用@classmethod装饰器定义,是最常规的方式:

class MathUtil:pi = 3.14159# 内部定义类方法@classmethoddef circle_area(cls, radius):# 使用cls访问类属性pireturn cls.pi * radius **2# 调用类方法(无需实例化,直接通过类调用)
print(MathUtil.circle_area(2))  # 输出:12.56636
(2)外部定义(动态绑定)

在类定义之外定义函数,再通过classmethod()转换为类方法并绑定到类上,实现动态扩展类的方法:

class Car:brand = "Unknown"# 外部定义一个函数(需接收cls作为第一个参数)
def set_brand(cls, new_brand):cls.brand = new_brand# 将外部函数转换为类方法并绑定到Car类
Car.set_brand = classmethod(set_brand)# 测试
print(Car.brand)  # 输出:Unknown
Car.set_brand("Tesla")
print(Car.brand)  # 输出:Tesla

总结

  • 类装饰器用于动态修改类的行为,本质是 “接收类、返回新类” 的函数;
  • 装饰器的核心是 “外部修改” 和 “动态扩展”,遵循开放 - 封闭原则;
  • 类方法可在类内部直接定义(@classmethod),也可在外部定义后动态绑定(classmethod()转换)

附录

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

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

作业

复习题1.
设计一个类装饰器validate_attrs

要求

  • 被装饰的类实例化时,自动校验__init__方法中传入的属性是否符合预设规则(例如:age必须为正数,name不能为空字符串)
  • 若属性不符合规则,抛出ValueError并提示具体错误
  • 不修改原类的__init__方法实现

复习题1解答

import functools
def validate_attrs(**validation_rules):def decorator(cls):original_init = cls.__init__@functools.wraps(original_init)def new_init(self,*args, **kwargs):for attr, rule in validation_rules.items():if attr in kwargs:if not rule(kwargs[attr]):raise ValueError(f"属性{attr}不合法:{kwargs[attr]}")original_init(self,*args,**kwargs)cls.__init__ = new_initreturn clsreturn decorator# 使用示例
@validate_attrs(age=lambda x: x > 0,  # age必须为正数name=lambda x: x.strip() != ""  # name不能是空字符串
)
class Person:def __init__(self, name, age):self.name = nameself.age = agetry:p1 = Person(name="Alice", age=20)print(f"创建成功: {p1.name}, {p1.age}岁")
except ValueError as e:print(f"创建失败: {e}")try:p2 = Person(name="", age=30)  # name不合法
except ValueError as e:print(f"创建失败: {e}")try:p3 = Person(name="Bob", age=-5)  # age不合法
except ValueError as e:print(f"创建失败: {e}")

创建成功: Alice, 20岁
创建失败: 属性name不合法:
创建失败: 属性age不合法:-5


复习题2.
分别用内部定义和外部定义两种方式,为Math类实现一个类方法sum,功能是计算两个数的和,并通过类直接调用。


内部定义方法解答

## 内部定义
class Math1:@classmethoddef sum(cls, a, b):result = a + breturn resultprint(Math1.sum(3, 5))  
print(Math1.sum(10, 20))  

8
30


外部定义方法解答

## 外部定义
def sum_external(cls, a, b):result = a + b return resultclass Math2:passMath2.sum = classmethod(sum_external)print(Math2.sum(3, 5))  # 输出:8
print(Math2.sum(10, 20))  # 输出:30

8
30


复习题3:为类添加实例缓存功能
设计一个类装饰器cache_instances,为被装饰的类添加实例缓存功能,要求:

当使用相同参数实例化类时,直接返回已缓存的实例,而非创建新实例
缓存的 key 由实例化时的参数(位置参数 + 关键字参数)共同决定
不修改原类的__init__方法实现

@cache_instances
class User:def __init__(self, user_id, name):self.user_id = user_idself.name = name# 测试
u1 = User(1, "Alice")
u2 = User(1, "Alice")  # 参数相同,应返回缓存的u1
u3 = User(2, "Bob")    # 参数不同,创建新实例print(u1 is u2)  # 输出:True(同一实例)
print(u1 is u3)  # 输出:False(不同实例)

复习题3解答

import functools
def cache_instances(cls):cache = {}def new_instance(*args, **kwargs):key = (args, tuple(sorted(kwargs.items())))if key not in cache:cache[key] = cls(*args, **kwargs)return cache[key]return new_instance@cache_instances
class User:def __init__(self, user_id , name):self.user_id = user_idself.name = nameu1 = User(1,'Alice')
u2 = User(1,'Alice')
u3 = User(2,'Bob')print(u1 is u2)
print(u1 is u3)

True
False


复习题4:扩展工具类的类方法
已知一个基础工具类StringUtil,包含一个内部定义的类方法reverse(用于反转字符串)。请完成以下任务:

完善StringUtil类,内部定义reverse方法:接收字符串参数,返回反转后的字符串
在类外部定义一个函数is_palindrome,功能是判断字符串是否为回文(正读和反读相同),并将其绑定为StringUtil的类方法
调用测试:通过类名直接调用两个方法,验证功能

print(StringUtil.reverse("hello"))  # 输出:"olleh"
print(StringUtil.is_palindrome("level"))  # 输出:True
print(StringUtil.is_palindrome("hello"))  # 输出:False

复习题4 解答

class StringUtil:@classmethoddef reverse(cls, org_str:str):n = len(org_str)new_str =''for i in range(n):new_str += org_str[n - 1 - i]return new_strdef is_palindrome(cls, s:str):if s == StringUtil.reverse(s):return Trueelse:return FalseStringUtil.is_palindrome = classmethod(is_palindrome)print(StringUtil.reverse("hello"))  # 输出:"olleh"
print(StringUtil.is_palindrome("level"))  # 输出:True
print(StringUtil.is_palindrome("hello"))  # 输出:False

olleh
True
False

@浙大疏锦行


文章转载自:

http://vS2debUQ.qqcdg.cn
http://yQDC0CVZ.qqcdg.cn
http://Sx3HfqlN.qqcdg.cn
http://4DvL5PHs.qqcdg.cn
http://4ZD3v4AI.qqcdg.cn
http://5SDlCSJC.qqcdg.cn
http://xaTki5yA.qqcdg.cn
http://DJyPTBzl.qqcdg.cn
http://LtnUyyT9.qqcdg.cn
http://NV9gDZow.qqcdg.cn
http://Kt4xDnAO.qqcdg.cn
http://fPXY76uw.qqcdg.cn
http://vcOCJr5k.qqcdg.cn
http://1IBc02IN.qqcdg.cn
http://gdfALxDy.qqcdg.cn
http://hiQVtzb2.qqcdg.cn
http://AmrL2T8D.qqcdg.cn
http://9ApUlYUr.qqcdg.cn
http://GjLnz6zN.qqcdg.cn
http://DkB7BW1m.qqcdg.cn
http://Xf7QvTnC.qqcdg.cn
http://kSHI91kt.qqcdg.cn
http://2NHLavmt.qqcdg.cn
http://AepUrLDg.qqcdg.cn
http://18g4ryOy.qqcdg.cn
http://E4zT3PEr.qqcdg.cn
http://uJsn4kvL.qqcdg.cn
http://FRIC95sl.qqcdg.cn
http://kCP6RfWd.qqcdg.cn
http://LVH6RuJz.qqcdg.cn
http://www.dtcms.com/a/382412.html

相关文章:

  • 2025.9.14英语红宝书【必背16-20】
  • 【CMake】环境变量
  • 贪心算法应用:广告投放优化问题详解
  • VSCode AI编程插件
  • 题解:P4711 「化学」相对分子质量
  • QGIS构建问题
  • 【飞书多维表格插件】
  • 云原生与多云策略:构建弹性、开放的数据底座
  • Java接口入门:从零掌握行为规范
  • Java基础常见知识点
  • Linux epoll 事件模型终极指南:深入解析 epoll_event 与事件类型
  • 简单学习HTML+CSS+JavaScript
  • 4 Python开发环境准备
  • 人源化抗体:从临床应用到未来趋势,3 大领域突破 + 4 大发展方向全解析
  • Scrapy框架入门:快速掌握爬虫精髓
  • 2.1线性表
  • Java 21 虚拟线程高并发落地:中间件适配、场景匹配与细节优化的技术实践
  • 炒股进阶理论知识
  • 07_Softmax回归、损失函数、分类
  • 复杂系统迭代中多变量测试的实施经验
  • 智能体综述:从 Agentic AI 到 AI Agent
  • MICAPS:气象信息综合分析与处理系统概述
  • Python中实现数据库事务回滚的方法
  • CodeAct范式
  • 有监督机器学习算法案例(Python)
  • MaxStateSuper 已经成功实现了输入与状态的统一
  • 技术面:Spring (bean的生命周期、创建方式、注入方式、作用域)
  • HUST-STAR电控组视觉任务
  • Redis 高并发方案适用的场景
  • 【开题答辩全过程】以 E家洁管理系统为例,包含答辩的问题和答案