Table of Contents
本文是我经过一晚上学习后对 Python 装饰器的理解总结。通过本文,你将掌握装饰器的核心概念、使用方法和实战技巧。
📚 参考资源 Link to 📚 参考资源
学习过程中参考了以下优质资源:
视频教程:B站UP主 码农高天的装饰器系列
文档教程:菜鸟教程 - Python 装饰器
🤔 什么是 Python 装饰器? Link to 🤔 什么是 Python 装饰器?
装饰器(Decorator) 是一种用于 修改或增强函数/类行为 的语法工具,通过 @ 符号实现。本质上利用了函数式编程和高阶函数的特性,可以在不修改原函数/类代码的前提下,为其添加新功能。
核心概念 Link to 核心概念
- 本质:装饰器本身是一个可调用对象(函数或类),接收一个函数/类作为参数,返回修改后的函数/类
- 作用:为代码提供非侵入式的功能扩展(如日志、权限校验、性能测试等)
- 语法糖:
@decorator等价于func = decorator(func)
🧩 理解函数的本质 Link to 🧩 理解函数的本质
要理解装饰器,首先需要理解:函数在 Python 中是一等公民(First-Class Object),这意味着函数可以像其他对象一样被传递和操作。
函数作为参数 Link to 函数作为参数
函数可以作为另一个函数的参数传递:
PYTHON
123456789101112def double(x):
return 2 * x
def triple(x):
return 3 * x
def calc(func, x):
"""接收一个函数作为参数"""
return func(x)
print(calc(double, 2)) # 输出: 4
print(calc(triple, 3)) # 输出: 9
函数作为返回值 Link to 函数作为返回值
函数也可以作为另一个函数的返回值:
PYTHON
123456789101112def generate_func(n):
"""返回一个新的函数"""
def multiple_func(x):
return n * x
return multiple_func
# 动态创建函数
double = generate_func(2)
triple = generate_func(3)
print(double(2)) # 输出: 4
print(triple(3)) # 输出: 9
🎯 装饰器的本质 Link to 🎯 装饰器的本质
装饰器就是输入和输出都是函数(或类)的高阶函数。
最简单的装饰器 Link to 最简单的装饰器
PYTHON
12345678910def dec(f):
"""最简单的装饰器,直接返回原函数"""
return f
@dec
def double(x):
return x * 2
# 等价于
double = dec(double)
第一个实用装饰器 Link to 第一个实用装饰器
下面是一个为函数添加前后处理逻辑的装饰器:
PYTHON
1234567891011121314def a_new_decorator(a_func):
"""在函数执行前后添加额外逻辑"""
def wrapTheFunction():
print("⏱️ 执行前的准备工作...")
a_func()
print("✅ 执行后的清理工作...")
return wrapTheFunction
@a_new_decorator
def a_function_requiring_decoration():
print("🎯 这是核心功能")
# 调用被装饰的函数
a_function_requiring_decoration()
输出:
PLAINTEXT
123⏱️ 执行前的准备工作...
🎯 这是核心功能
✅ 执行后的清理工作...
🔧 正确使用 functools.wraps Link to 🔧 正确使用 functools.wraps
使用装饰器时,被装饰函数的元信息(如函数名、文档字符串)会被覆盖。为了保留这些信息,需要使用 functools.wraps。
问题演示 Link to 问题演示
PYTHON
1234567891011def a_new_decorator(a_func):
def wrapTheFunction():
a_func()
return wrapTheFunction
@a_new_decorator
def my_function():
"""我是文档字符串"""
pass
print(my_function.__name__) # 输出: wrapTheFunction ❌ 错误!
解决方案 Link to 解决方案
PYTHON
12345678910111213141516from functools import wraps
def a_new_decorator(a_func):
@wraps(a_func) # ✅ 保留原函数的元信息
def wrapTheFunction():
print("执行前...")
a_func()
print("执行后...")
return wrapTheFunction
@a_new_decorator
def my_function():
"""我是文档字符串"""
pass
print(my_function.__name__) # 输出: my_function ✅ 正确!
💼 装饰器的实战场景 Link to 💼 装饰器的实战场景
1️⃣ 标准装饰器模板 Link to 1️⃣ 标准装饰器模板
PYTHON
1234567891011121314151617181920from functools import wraps
def decorator_name(f):
@wraps(f)
def decorated(*args, **kwargs):
# 执行前的逻辑
if not can_run:
return "Function will not run"
# 调用原函数
result = f(*args, **kwargs)
# 执行后的逻辑
return result
return decorated
@decorator_name
def func():
return "Function is running"
2️⃣ 性能计时器 Link to 2️⃣ 性能计时器
记录函数执行时间的装饰器:
PYTHON
1234567891011121314151617181920import time
from functools import wraps
def timeit(f):
"""计算函数执行时间"""
@wraps(f)
def wrapper(*args, **kwargs):
start = time.time()
result = f(*args, **kwargs)
elapsed = time.time() - start
print(f"⏱️ {f.__name__} 执行时间: {elapsed:.4f} 秒")
return result
return wrapper
@timeit
def slow_function():
time.sleep(1)
return "Done!"
slow_function() # 输出: ⏱️ slow_function 执行时间: 1.0012 秒
3️⃣ 权限验证 Link to 3️⃣ 权限验证
用于 Web 应用的授权装饰器:
PYTHON
123456789101112131415from functools import wraps
def requires_auth(f):
"""检查用户是否已授权"""
@wraps(f)
def decorated(*args, **kwargs):
auth = request.authorization
if not auth or not check_auth(auth.username, auth.password):
return authenticate() # 返回 401 错误
return f(*args, **kwargs)
return decorated
@requires_auth
def protected_route():
return "这是受保护的页面"
4️⃣ 日志记录 Link to 4️⃣ 日志记录
自动记录函数调用的装饰器:
PYTHON
12345678910111213141516171819from functools import wraps
def logit(func):
"""记录函数调用"""
@wraps(func)
def with_logging(*args, **kwargs):
print(f"📝 调用函数: {func.__name__}")
print(f" 参数: args={args}, kwargs={kwargs}")
result = func(*args, **kwargs)
print(f" 返回值: {result}")
return result
return with_logging
@logit
def add(x, y):
"""计算两数之和"""
return x + y
result = add(3, 5)
输出:
PLAINTEXT
123📝 调用函数: add
参数: args=(3, 5), kwargs={}
返回值: 8
🎨 带参数的装饰器 Link to 🎨 带参数的装饰器
有时我们需要给装饰器传递参数,这需要在装饰器外部再包裹一层函数。
带参数的日志装饰器 Link to 带参数的日志装饰器
PYTHON
12345678910111213141516171819202122232425262728from functools import wraps
def logit(logfile='out.log'):
"""可配置日志文件的装饰器"""
def logging_decorator(func):
@wraps(func)
def wrapped_function(*args, **kwargs):
log_string = f"{func.__name__} was called"
print(log_string)
# 写入日志文件
with open(logfile, 'a') as f:
f.write(log_string + '\n')
return func(*args, **kwargs)
return wrapped_function
return logging_decorator
@logit() # 使用默认文件 out.log
def myfunc1():
pass
@logit(logfile='func2.log') # 自定义日志文件
def myfunc2():
pass
myfunc1() # 日志写入 out.log
myfunc2() # 日志写入 func2.log
🏛️ 类装饰器 Link to 🏛️ 类装饰器
装饰器不仅可以是函数,还可以是类!类装饰器通过实现 __call__ 方法来工作。
基础类装饰器 Link to 基础类装饰器
PYTHON
12345678910111213141516171819202122232425from functools import wraps
class logit(object):
def __init__(self, logfile='out.log'):
self.logfile = logfile
def __call__(self, func):
@wraps(func)
def wrapped_function(*args, **kwargs):
log_string = f"{func.__name__} was called"
print(log_string)
# 写入日志
with open(self.logfile, 'a') as f:
f.write(log_string + '\n')
# 调用子类可能的扩展方法
self.notify()
return func(*args, **kwargs)
return wrapped_function
def notify(self):
"""空方法,供子类重写"""
pass
继承扩展类装饰器 Link to 继承扩展类装饰器
PYTHON
1234567891011121314151617class email_logit(logit):
"""
扩展 logit,添加邮件通知功能
"""
def __init__(self, email='admin@myproject.com', *args, **kwargs):
self.email = email
super(email_logit, self).__init__(*args, **kwargs)
def notify(self):
"""发送邮件通知"""
# 实际项目中这里会发送邮件
print(f"📧 发送通知到: {self.email}")
@email_logit()
def critical_function():
"""关键函数,需要邮件通知"""
print("执行关键操作...")
📋 总结 Link to 📋 总结
装饰器是 Python 中强大而优雅的特性,掌握装饰器能让你写出更简洁、可维护的代码。
核心要点 Link to 核心要点
- ✅ 装饰器本质是高阶函数,接收函数返回函数
- ✅ 使用
@wraps保留原函数的元信息 - ✅ 装饰器常用于日志、计时、权限验证、缓存等场景
- ✅ 带参数的装饰器需要三层嵌套
- ✅ 类装饰器通过
__call__方法实现,便于扩展
最佳实践 Link to 最佳实践
- 始终使用
@wraps:保留函数元信息 - 使用
*args, **kwargs:支持任意参数 - 合理命名:装饰器名称应清晰表达功能
- 文档字符串:为装饰器添加清晰的说明
- 避免过度使用:不要滥用装饰器,保持代码可读性
进阶学习 Link to 进阶学习
装饰器是 Python 的精华之一,希望这篇教程能帮助你深入理解并灵活运用!💪
Thanks for reading!
Python 装饰器完全指南
© EveSunMaple | CC BY-SA 4.0