Python 装饰器:从基础到语法糖
·
装饰器是 Python 中一个非常实用且常用的高级特性,它能够在不修改已有函数源代码和调用方式的前提下,为函数扩展额外的功能。本文将带你从基础概念出发,逐步理解装饰器的原理、写法、语法糖以及实际应用。
1. 什么是装饰器?
装饰器本质上是一个闭包函数,它接收一个函数作为参数,并返回一个新的函数(通常是内部函数)。这个新函数会在执行原函数的基础上,增加一些额外的功能。
装饰器的核心思想:
- 不修改已有函数的源代码
- 不修改已有函数的调用方式
- 通过"包装"的方式为函数增加功能
2. 从闭包到装饰器
2.1 闭包回顾
闭包是指在一个内部函数中引用了外部函数的变量,并且外部函数返回这个内部函数。装饰器是闭包的一种特殊形式,区别在于:
- 闭包:外部函数接收的是普通变量
- 装饰器:外部函数接收的是一个函数对象
2.2 基础装饰器写法
def decorator(func):
"""基础装饰器示例"""
def inner():
print("我是装饰器,我增加了新的功能")
func() # 执行原函数
return inner
def comment():
print("这是一个发表评论的功能")
# 使用装饰器
comment = decorator(comment)
comment()
输出:
我是装饰器,我增加了新的功能
这是一个发表评论的功能
3. 装饰器的工作原理
3.1 执行过程分析
decorator(comment)返回内部函数innercomment = decorator(comment)将原函数名重新指向inner函数- 调用
comment()实际上调用的是inner(),其中包含了原函数的功能和新增功能
3.2 为什么不能直接修改原函数?
# ❌ 错误做法:直接修改源代码
def comment():
print("我要增加新的功能") # 直接添加代码
print("这是一个发表评论的功能")
# ❌ 错误做法:改变调用方式
def comment():
print("这是一个发表评论的功能")
def enhanced_comment(): # 创建新函数
print("新增功能")
comment()
这两种做法都不符合装饰器的设计理念,因为它们要么修改了源代码,要么改变了调用方式。
4. 装饰器语法糖
为了简化装饰器的使用,Python 提供了 @ 语法糖:
def decorator(func):
def inner():
print("我是装饰器,我增加了新的功能")
func()
return inner
@decorator # 装饰器语法糖
def comment():
print("这是一个发表评论的功能")
comment()
语法糖的执行原理:@decorator 等价于 comment = decorator(comment),只是写法更加简洁优雅。
5. 带参数的装饰器
5.1 装饰带参数的函数
def decorator(func):
def inner(*args, **kwargs):
print("函数执行前...")
result = func(*args, **kwargs)
print("函数执行后...")
return result
return inner
@decorator
def greet(name):
print(f"Hello, {name}!")
return f"Greeted {name}"
result = greet("Alice")
print(f"返回值: {result}")
5.2 带参数的装饰器
def repeat(times):
"""重复执行指定次数的装饰器"""
def outer_wrapper(func):
def inner(*args, **kwargs):
results = []
for i in range(times):
print(f"第{i+1}次执行:")
result = func(*args, **kwargs)
results.append(result)
return results
return inner
return outer_wrapper
@repeat(times=3)
def say_hello(name):
print(f"Hello, {name}!")
return f"said hello to {name}"
say_hello("Bob")
6. 实际应用示例
6.1 计时装饰器
import time
def timer_decorator(func):
"""计时装饰器"""
def wrapper():
start_time = time.time()
func()
end_time = time.time()
print(f"完成这个任务,消耗的时间为:{end_time - start_time:.6f}秒")
return wrapper
@timer_decorator
def my_daily_work():
"""爱你三千遍功能"""
for i in range(3000):
print("I LOVE YOU")
my_daily_work()
6.2 权限验证装饰器
def require_login(func):
"""登录验证装饰器"""
def wrapper(user, *args, **kwargs):
if not user.get('is_authenticated', False):
print("错误:用户未登录,请先登录!")
return None
return func(user, *args, **kwargs)
return wrapper
@require_login
def view_profile(user):
"""查看用户资料"""
print(f"用户名: {user['username']}")
print(f"邮箱: {user['email']}")
return user
# 测试
user1 = {'username': 'alice', 'email': 'alice@example.com', 'is_authenticated': True}
user2 = {'username': 'bob', 'email': 'bob@example.com', 'is_authenticated': False}
view_profile(user1) # 正常执行
view_profile(user2) # 提示未登录
7. 多个装饰器的执行顺序
def decorator1(func):
def wrapper():
print("装饰器1 - 前")
func()
print("装饰器1 - 后")
return wrapper
def decorator2(func):
def wrapper():
print("装饰器2 - 前")
func()
print("装饰器2 - 后")
return wrapper
@decorator1
@decorator2
def my_function():
print("原始函数")
my_function()
输出:
装饰器1 - 前
装饰器2 - 前
原始函数
装饰器2 - 后
装饰器1 - 后
执行顺序:从下往上,从内到外。相当于 my_function = decorator1(decorator2(my_function))。
8. 使用 functools.wraps 保留元信息
使用 functools.wraps 可以保留原函数的名称、文档字符串等元信息:
import functools
def my_decorator(func):
@functools.wraps(func) # 保留原函数的元信息
def wrapper(*args, **kwargs):
"""包装函数"""
print(f"调用函数: {func.__name__}")
return func(*args, **kwargs)
return wrapper
@my_decorator
def example():
"""这是一个示例函数"""
return "Hello"
print(example.__name__) # 输出: example(而不是 wrapper)
print(example.__doc__) # 输出: 这是一个示例函数
9. 总结
装饰器是 Python 中非常强大的工具,它通过闭包的机制实现了对函数的"装饰"功能。掌握装饰器可以帮助你:
- 写出更加模块化和可复用的代码
- 实现横切关注点(如日志、权限、性能监控)的分离
- 保持代码的整洁和可维护性
- 遵循开放-封闭原则(对扩展开放,对修改封闭)
记住装饰器的核心:不修改源代码,不改变调用方式,只增加功能。通过合理使用装饰器,你可以大幅提升代码的质量和开发效率。
扩展阅读建议:
- 深入学习
functools模块的其他装饰器(如lru_cache) - 了解 Python 内置装饰器(如
@classmethod,@staticmethod,@property) - 探索装饰器在 Web 框架(如 Flask、Django)中的应用
更多推荐



所有评论(0)