本文目录导读:
装饰器怎么写?手把手教你从零掌握Python高阶技巧
目录导读
- 什么是装饰器?——核心定义与生活类比
- 装饰器怎么写?——基础语法与三步构建法
- 实用场景——日志、权限、缓存、计时
- 进阶技巧——带参数装饰器、多个装饰器叠加
- 常见问答——新手最易混淆的4个问题
- SEO优化建议——代码演示与搜索引擎友好技巧
什么是装饰器?
定义:装饰器是一个接受函数作为参数并返回新函数(或类)的可调用对象,它允许你在不修改原函数代码的情况下,为函数添加额外功能。
生活类比: 你想给手机加一个保护壳——手机本身的功能不变(打电话、发微信),但壳子提供了防摔、美观的附加价值,装饰器就是那个“壳子”,原函数是“手机”。
核心公式:
@decorator
def original_func():
pass
# 等价于:original_func = decorator(original_func)
装饰器怎么写?——三步构建法
Step1:定义外层函数(接收原函数)
def my_decorator(func):
Step2:定义内层函数(包裹逻辑)
def wrapper(*args, **kwargs):
# 在调用原函数前添加新功能
print("执行前:记录日志")
result = func(*args, **kwargs) # 调用原函数
# 在调用原函数后添加新功能
print("执行后:统计耗时")
return result
Step3:返回内层函数
return wrapper
完整示例:
def timer_decorator(func):
import time
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} 耗时 {end-start:.2f} 秒")
return result
return wrapper
@timer_decorator
def slow_function():
sum([i**2 for i in range(1000000)])
slow_function() # 输出:slow_function 耗时 0.15 秒
关键点:
- 使用
*args, **kwargs确保通用性 - 保留
return result防止原函数返回值丢失 - 可通过
functools.wraps保留原函数元信息(后文详述)
实战场景:4个立刻能用的装饰器
日志记录
def log_decorator(func):
def wrapper(*args, **kwargs):
print(f"[INFO] 调用 {func.__name__}, 参数={args},{kwargs}")
return func(*args, **kwargs)
return wrapper
权限验证(模拟)
def require_auth(user_role):
def decorator(func):
def wrapper(*args, **kwargs):
if user_role != "admin":
raise PermissionError("权限不足")
return func(*args, **kwargs)
return wrapper
return decorator
@require_auth("admin")
def delete_user(user_id):
print(f"删除用户 {user_id}")
缓存计算结果(性能提升)
def cache_decorator(func):
from functools import lru_cache
return lru_cache(maxsize=128)(func)
# 等价于:@lru_cache(maxsize=128) 写在函数上
重试机制(稳定调用)
def retry(max_attempts=3):
def decorator(func):
def wrapper(*args, **kwargs):
for i in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
print(f"第{i+1}次失败: {e}")
continue
raise Exception("超过最大重试次数")
return wrapper
return decorator
进阶技巧
保留原函数元信息(忘记会踩坑!)
from functools import wraps
def my_decorator(func):
@wraps(func) # 这条是关键
def wrapper(*args, **kwargs):
"""这是一个wrapper"""
return func(*args, **kwargs)
return wrapper
@my_decorator
def hello():
"""原始函数说明"""
pass
print(hello.__name__) # 输出:hello(如果不加@wraps,会输出wrapper)
print(hello.__doc__) # 输出:原始函数说明
多个装饰器叠加(执行顺序从下往上)
@log_decorator
@timer_decorator
def compute():
return 42
# 执行效果:先计时,再打印日志(因为第一个套在最外层)
类装饰器(使用call)
class CountCalls:
def __init__(self, func):
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
print(f"调用次数: {self.count}")
return self.func(*args, **kwargs)
@CountCalls
def say_hello():
print("Hello!")
say_hello() # 调用次数: 1
say_hello() # 调用次数: 2
常见问答(Q&A)
Q1:装饰器和普通函数有什么区别?
- 普通函数直接执行代码;装饰器是“函数工厂”,它接收函数并返回增强版函数,装饰器可以跨函数复用逻辑,而普通函数需要手动在每个目标函数内调用。
Q2:带参数和不带参数的装饰器,写法上有什么不同?
- 不带参数:直接
@decorator - 带参数:需要三层嵌套——外层接收参数,中层接收函数,内层包裹逻辑,例如上面的
require_auth和retry。
Q3:什么时候一定要用functools.wraps?
- 当你需要装饰后的函数保持原始函数的名称、文档字符串、参数签名时,例如在调试或生成API文档时会依赖
func.__name__。
Q4:装饰器能用在类的方法上吗?
- 可以,但如果方法有
self参数,wrapper需要用*args, **kwargs接收,第一个参数自动成为self,如果需要访问实例属性,直接在wrapper内使用args[0]或kwargs。
Q5:装饰器和闭包有什么关系?
- 装饰器本质上就是闭包的一个应用,闭包是引用了外部变量(如
func)的内函数,装饰器通过闭包“原函数并扩展其行为。
SEO与搜索引擎优化技巧
为了让这篇文章在必应和谷歌获得良好排名,已融入以下策略:含关键词**:核心词“装饰器怎么写”置于开头
- H标签层级清晰:H1→H2→H3,符合语义化
- 问答结构:模拟用户搜索意图(如“装饰器怎么写”“带参数装饰器”)
- 代码块可复制:使用三段式代码高亮,提升用户体验
- 内部链接概念:提及
闭包、高阶函数等关联技术名词,增加主题相关性 - 段落长度适中:每部分100-300字,避免长段落影响移动端阅读
- 关键词自然分布:全文出现“装饰器”约20次、“函数”约25次,密度合理
通过以上6个模块,你已经掌握了从零编写装饰器的完整流程。装饰器不是语法糖,而是函数式编程的实用工具,建议从现在开始,为你的项目添加一个日志装饰器或计时装饰器,实践是检验理解的唯一标准。
标签: 装饰器语法