太棒了!欢迎来到 【跟着AI学Python】进阶第4天:装饰器与上下文管理器!🎉今天我们将学习两种让代码更优雅、更安全、更可复用的高级技巧:
💡 装饰器(Decorator) → 无侵入地增强函数功能💡 上下文管理器(Context Manager) → 自动管理资源(如文件、锁、数据库连接)
🎯 今日目标
✅ 掌握 函数装饰器 的编写与使用✅ 了解 类装饰器 的基本形式✅ 理解 上下文管理器协议(__enter__, __exit__)✅ 实战:计时装饰器 + 安全文件读取器
📘 一、核心概念速览
🔧 二、实践 1:编写计时装饰器
✅ 目标:测量任意函数的执行时间
方式 1:基础函数装饰器
# decorators.pyimport timefrom functools import wrapsdef timer(func): """计时装饰器:打印函数执行时间""" @wraps(func) # 保留原函数元信息(如 __name__, __doc__) def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) end = time.time() print(f"⏱️ {func.__name__} 耗时: {end - start:.4f} 秒") return result return wrapper# 使用示例@timerdef slow_function(): time.sleep(1) return "完成"print(slow_function()) # 输出: ⏱️ slow_function 耗时: 1.0042 秒
🔍 关键点:
@wraps(func)防止装饰后函数名变成 wrapper*args, **kwargs 使装饰器通用
方式 2:带参数的装饰器(进阶)
def timer_with_label(label="执行"): """可自定义标签的计时装饰器""" def decorator(func): @wraps(func) def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) end = time.time() print(f"⏱️ [{label}] {func.__name__}: {end - start:.4f} 秒") return result return wrapper return decorator# 使用@timer_with_label("数据处理")def process_data(): time.sleep(0.5)process_data() # 输出: ⏱️ [数据处理] process_data: 0.5021 秒
🔧 三、实践 2:类装饰器(了解即可)
class CountCalls: """类装饰器:统计函数调用次数""" def __init__(self, func): self.func = func self.count = 0 def __call__(self, *args, **kwargs): self.count += 1 print(f"📞 {self.func.__name__} 已被调用 {self.count} 次") return self.func(*args, **kwargs)@CountCallsdef say_hello(): print("Hello!")say_hello() # 📞 say_hello 已被调用 1 次say_hello() # 📞 say_hello 已被调用 2 次
✅ 适用场景:需要保存状态(如计数、缓存)的装饰逻辑
🔧 四、实践 3:自定义文件读取上下文管理器
❌ 危险写法(资源可能未释放)
def read_file_bad(filename): f = open(filename) data = f.read() # 如果这里发生异常,f.close() 不会执行! f.close() return data
✅ 安全写法 1:使用内置 with open
def read_file_good(filename): with open(filename) as f: return f.read() # 自动调用 f.close()
✅ 安全写法 2:自定义上下文管理器类
# context_managers.pyclass SafeFileReader: """自定义文件读取上下文管理器""" def __init__(self, filename, mode = 'r', encoding="utf-8"): if mode not in ('r', 'w', 'a', 'r+', 'w+', 'a+'): raise ValueError(f"不支持的文件模式: {mode}") self.filename = filename self.mode = mode self.encoding = encoding self.file = None def __enter__(self): """进入 with 块时打开文件""" action = { 'r': '读取', 'w': '写入(覆盖)', 'a': '追加', 'r+': '读写', 'w+': '读写(覆盖)', 'a+': '读写(追加)' }.get(self.mode, '操作') """进入 with 块时调用""" print(f"📂 打开文件: {self.filename}") self.file = open(self.filename,mode=self.mode, encoding=self.encoding) return self.file # 返回给 as 变量 def __exit__(self, exc_type, exc_val, exc_tb): """退出 with 块时调用(无论是否异常)""" if self.file: self.file.close() print(f"🗑️ 关闭文件: {self.filename}") # 返回 False 表示不抑制异常(默认行为) return False# 使用try: with SafeFileReader("test.txt") as f: content = f.read() print("内容:", content[:50])except FileNotFoundError: print("❌ 文件未找到")
🔑 __exit__ 参数说明:
exc_type:异常类型(如 FileNotFoundError)exc_val:异常实例
🔧 五、结合银行系统:实战应用
场景 1:用装饰器记录关键操作耗时
# bank.pyfrom decorators import timerclass BankAccount: # ... @timer def deposit(self, amount): # 模拟复杂校验(实际可能涉及风控、日志等) time.sleep(0.01) self._balance += amount return True
场景 2:用上下文管理器安全加载账户
# bank.pyfrom context_managers import SafeFileReaderimport jsondef load_accounts(): try: with SafeFileReader("data/accounts.jsonl") as f: for line in f: yield json.loads(line.strip()) except FileNotFoundError: logger.warning("账户文件不存在,返回空") return
🧪 六、完整测试代码
# main.pyfrom decorators import timer, timer_with_labelfrom context_managers import SafeFileReader# 测试装饰器@timerdef calculate_sum(n): return sum(range(n))result = calculate_sum(1000000) # ⏱️ calculate_sum 耗时: 0.0421 秒# 测试上下文管理器with SafeFileReader("example.txt", "w") as f: f.write("Hello, Context Manager!")with SafeFileReader("example.txt") as f: print("读取内容:", f.read()) # 📂 打开... 🗑️ 关闭...
🧠 七、关键知识点总结
1. 装饰器本质
@timerdef foo(): pass# 等价于foo = timer(foo)
2. 上下文管理器协议
- 必须实现
__enter__ 和 __exit__ with obj as x
3. 何时使用?
📝 小结:优雅与安全
装饰器 → 让功能增强像“穿衣服”一样简单上下文管理器 → 让资源管理像“呼吸”一样自然
你的代码将因此:
🎉 恭喜完成装饰器与上下文管理器进阶!你已掌握 Python 高阶编程的两大利器!
继续加油,你的代码正在变得越来越专业!✨