✅ 像AOP一样理解它 —— @staticmethod、@classmethod、自定义装饰器

▲ 装饰器就是在不修改原函数的情况下,给它加功能
一、开篇:Java的注解 + 代理模式 = Python的装饰器 🎯
Java 里有 AOP(面向切面编程),用注解 + 代理模式在方法前后插入逻辑,比如 @Transactional、@Cacheable。Python 的装饰器是同样的思路,但实现更直接:函数套函数。
今天我们要掌握:
• 装饰器原理:函数作为参数、闭包• 内置装饰器:@staticmethod、@classmethod• 自定义装饰器:计时器、日志记录• 带参数的装饰器:更灵活的控制• 对比 Java 的注解和 AOP
二、装饰器原理:函数套函数 🧅
Python 里函数是一等公民,可以当参数传递、当返回值返回。装饰器就是利用这个特性,用一个函数包装另一个函数。
# 最简单的装饰器:在函数执行前后打印日志 def my_decorator(func): def wrapper(*args, **kwargs): print(f"调用 {func.__name__} 开始") result = func(*args, **kwargs) print(f"调用 {func.__name__} 结束") return result return wrapper # 使用装饰器 @my_decorator def say_hello(name): print(f"你好,{name}!") say_hello("张三") # 输出: # 调用 say_hello 开始 # 你好,张三! # 调用 say_hello 结束 # 对比Java: // @Aspect + @Around 实现类似功能 // 需要引入 Spring AOP,配置切点表达式// Python 的装饰器是纯语法糖,不需要框架支持
@my_decorator 这行语法糖,等价于 say_hello = my_decorator(say_hello)。装饰器本质上就是函数重新赋值。
💡 Java程序员的坑: • 装饰器在函数定义时执行,不是在调用时 • 被装饰的函数名会变成 wrapper,用 functools.wraps 保留原函数信息 • *args 和 **kwargs 是万能参数,保证装饰器能套任何函数• 装饰器可以叠加,@a @b 等价于 a(b(func))
三、内置装饰器:@staticmethod 和 @classmethod 🏷️
Python 类里有两个常用装饰器,对标 Java 的 static 方法和类方法。
class Student: school = "清华大学" def __init__(self, name): self.name = name # 实例方法:第一个参数是 self def info(self): return f"{self.name} 在 {self.school}" @staticmethod def is_valid_name(name): # 静态方法:不依赖实例,不需要 self return len(name) > 0 @classmethod def from_dict(cls, data): # 类方法:第一个参数是 cls,可以访问类属性 return cls(data["name"]) # 使用 print(Student.is_valid_name("张三")) # True s = Student.from_dict({"name": "李四"}) print(s.info()) # 对比Java: // public static boolean isValidName(String name) { ... } // Python 的 @staticmethod 和 Java 的 static 方法概念一样// @classmethod 类似 Java 的工厂方法,但第一个参数是类本身
@staticmethod 和 Java 的 static 方法几乎一样,不依赖实例。@classmethod 第一个参数是 cls,可以创建类的实例,常用于工厂模式。
四、实用装饰器:计时器和缓存 ⏱️
装饰器最常见的两个应用场景:统计函数执行时间、缓存重复计算的结果。
import time from functools import lru_cache # 1. 计时器装饰器 def timer(func): def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) print(f"{func.__name__} 耗时: {time.time()-start:.4f}秒") return result return wrapper @timer def slow_function(): time.sleep(1) return "done" # 2. 缓存装饰器(Python内置) @lru_cache(maxsize=128) def fibonacci(n): if n < 2: return n return fibonacci(n-1) + fibonacci(n-2) # 对比Java: // 计时器:用 AOP 的 @Around // 缓存:用 Spring 的 @Cacheable// Python 的 lru_cache 是内置的,不需要引入框架
lru_cache 是 Python 内置的缓存装饰器,maxsize 控制缓存大小。它用字典存结果,相同的输入直接返回缓存值,避免重复计算。斐波那契数列用缓存后,复杂度从指数降到线性。
五、带参数的装饰器:更灵活的控制 ⚙️
有时候装饰器本身需要参数,比如 @retry(max_attempts=3)。这需要三层嵌套函数。
# 带参数的装饰器:重试机制 def retry(max_attempts=3, delay=1): def decorator(func): def wrapper(*args, **kwargs): for attempt in range(max_attempts): try: return func(*args, **kwargs) except Exception as e: print(f"第{attempt+1}次失败: {e}") time.sleep(delay) raise Exception("全部重试失败") return wrapper return decorator @retry(max_attempts=3, delay=1) def call_api(): import random if random.random() < 0.7: raise Exception("网络错误") return "API响应" # 对比Java: // Spring Retry 的 @Retryable(maxAttempts=3)// Python 的装饰器自己写,不需要引入额外依赖
带参数的装饰器需要三层函数:第一层接收装饰器参数,第二层接收被装饰函数,第三层是实际包装逻辑。看起来复杂,但套路固定,记住模板就行。
六、综合对比:Java vs Python 装饰器 📊
七、今日实战:AI模型调用计时器 🎯
import time from functools import wraps # 计时器装饰器(保留原函数信息) def timer(func): @wraps(func) def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) elapsed = time.time() - start print(f"[{func.__name__}] 耗时: {elapsed:.3f}秒") return result return wrapper # 模拟AI模型调用 class AIModel: @staticmethod def validate_input(text): return len(text) > 0 @timer def predict(self, text): time.sleep(0.5) # 模拟推理耗时 return f"预测结果: {text[:10]}..." model = AIModel() if AIModel.validate_input("今天天气真好"): result = model.predict("今天天气真好,适合出门")
✅ 这个练习涵盖了今天所有知识点: • 自定义装饰器(计时器) • @wraps 保留原函数信息 • @staticmethod 静态方法• 装饰器在类方法上的应用
八、今日任务 & 明天预告 🚀
📌 今日任务
✅ 写一个打印函数入参和返回值的装饰器 ✅ 用 @staticmethod 写一个工具方法 ✅ 用 @lru_cache 缓存一个递归函数 ✅ 写一个带参数的装饰器(如重试次数) ✅ 对比 Java 的 @注解 和 Python 的 @装饰器
📌 明天(第26课)预告
常用标准库:os/sys/datetime/random
我们会聊: • 路径操作、命令行参数 • 时间处理、随机数 • 对比 Java 的对应类
📌 所有软件和后续代码流程都免费分享,有需要关注后发送ai课程即可。本节课程源码和教学视频已经放在老位置了,刷新后即可获取!建议将我设为星标,避免错过每日课程。坚持180节课,预祝我们转型成功!
如果这篇文章对你有帮助,欢迎点赞、收藏、转发给身边的Java同行。明天见!👋
夜雨聆风