摘要:def require_admin(func):def wrapper(user, *args, **kwargs):if user.get("role") != "admin":raise PermissionError("权限不足,需要 admin 身份"
在实际开发中,函数装饰器往往需要根据不同场景调整行为。要做到这一点,就必须让装饰器本身接收参数——这就是带参数的装饰器。
一、为什么需要带参数的装饰器?
假设我们正在开发一个系统,其中某些功能只能由管理员使用,比如删除用户。
如果采用普通装饰器,可能会这样写:
def require_admin(func):def wrapper(user, *args, **kwargs):if user.get("role") != "admin":raise PermissionError("权限不足,需要 admin 身份")return func(user, *args, **kwargs)return wrapper@require_admindef delete_user(user, uid):print(f"用户 {uid} 已被 {user['name']} 删除。")问题在于:
require_admin 只能处理管理员的权限。如果后续还有老师权限、学生权限等场景,就不得不再写 require_teacher、require_student ……
这不仅会造成逻辑重复,还缺乏灵活性。不同的功能需要根据不同的场景调整权限要求,而固定逻辑的装饰器显然无法满足。
更优雅的做法是:编写一个通用的带参数装饰器,在使用时传入所需角色,从而在不同场景下实现灵活配置。
二、带参数的函数装饰器语法结构
带参数的函数装饰器实际上是三层嵌套函数:
工厂函数(接收参数) → 装饰器函数(接收目标函数) → 包装函数(执行逻辑)。
import functoolsdef 装饰器工厂(参数...): # 外层函数:接收装饰器参数def 装饰器(func): # 中层函数:接收被装饰函数@functools.wraps(func) # 保留元信息(函数名、文档字符串等)def wrapper(*args, **kwargs): # 内层函数:包装逻辑# 在这里可使用“外层参数”控制行为result = func(*args, **kwargs)return resultreturn wrapperreturn 装饰器执行顺序:
1、当 Python 解释器帅遇到 @装饰器工厂(xxx) 时,先执行外层工厂函数,得到一个真正的装饰器。
2、这个装饰器接收目标函数,返回包装函数 wrapper。
3、每次调用目标函数时,实际执行的都是 wrapper,它会在合适的时机调用原函数。
三、示例:权限验证装饰器
前面我们看到固定写死 admin 的装饰器不够灵活,现在通过带参数的写法,我们就能同时支持 admin、teacher、student 等多种角色。
import functools# 外层函数:接收所需角色def require_role(role): # 中层函数:接收目标函数def decorator(func): @functools.wraps(func)def wrapper(user, *args, **kwargs): # 在这里检查用户权限if user.get("role") != role:raise PermissionError(f"用户 {user['name']} 权限不足,需要 {role} 才能执行。")# 权限符合,执行原函数return func(user, *args, **kwargs)return wrapperreturn decorator# ===== 示例函数们 =====@require_role("admin") # 只有管理员能删除用户def delete_user(user, uid):print(f"✅ {user['name']} 删除了用户 {uid}。")@require_role("teacher") # 只有老师能批改作业def grade_homework(user, homework):print(f"✅ {user['name']} 批改了作业:{homework}")@require_role("student") # 只有学生能提交作业def submit_homework(user, homework):print(f"✅ {user['name']} 提交了作业:{homework}")# ===== 测试用户 =====admin = {"name": "陈工", "role": "admin"} # 管理员teacher = {"name": "张老师", "role": "teacher"} # 老师student = {"name": "周同学", "role": "student"} # 学生# 测试调用delete_user(admin, 101) # ✅ 成功grade_homework(teacher, "数学作业") # ✅ 成功submit_homework(student, "Python 小练习") # ✅ 成功# 错误示例:权限不符会报错delete_user(student, 102) # ❌ PermissionError运行结果:
✅ 陈工(管理员) 删除了用户 101。✅ 张老师 批改了作业:数学作业✅ 周同学 提交了作业:Python 小练习Traceback (most recent call last):...PermissionError: 用户 周同学 权限不足,需要 admin 才能执行。可以看到:
同一个装饰器 @require_role(...),通过传入不同的参数(admin、teacher、student),就能灵活地应用到不同函数上。
如果继续扩展更多角色,也只需要在使用时改参数,而不用重复写多个装饰器。
小结
带参数的函数装饰器解决了“固定逻辑”的问题,让装饰器在不同场景下可配置。
通过这种方式,我们能避免重复写多个几乎相同的装饰器,从而让代码更加通用、灵活。
掌握带参数装饰器,也是理解更复杂装饰器(如缓存、重试、限流)的关键一步。
“点赞有美意,赞赏是鼓励”
来源:人尔个个