乐于分享
好东西不私藏

Java+AI 第25课 装饰器

Java+AI 第25课 装饰器

✅ 像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 装饰器 📊

特性
Java
Python
实现方式
注解 + AOP代理
函数装饰器
依赖
需要Spring等框架
纯语法,无依赖
静态方法
static关键字
@staticmethod
缓存
@Cacheable
@lru_cache
自定义难度
较高(需了解AOP)
较低(函数套函数)

七、今日实战: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同行。明天见!👋