Python 实现一个装饰器函数(手把手讲解)
💡一则或许对你有用的小广告
欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...
,点击查看项目介绍 ;演示链接: http://116.62.199.48:7070 ;- 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;
截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观
在 Python 开发中,装饰器(Decorator)是一个强大且灵活的工具,它能帮助开发者以非侵入的方式修改或扩展函数的行为。无论是初学者还是中级开发者,掌握如何“Python 实现一个装饰器函数”都将大幅提升代码的复用性和可维护性。本文将从基础概念入手,逐步深入讲解装饰器的实现原理,并通过多个实际案例展示其应用场景。
装饰器的底层逻辑:函数即对象
在 Python 中,函数本质上是“对象”(Object)。这意味着函数可以像普通变量一样被赋值、传递,甚至作为参数传递给其他函数。这一特性是理解装饰器的关键。
示例 1:函数赋值与传递
def greet(name):
return f"Hello, {name}!"
greeting_func = greet
print(greeting_func("Alice")) # 输出:Hello, Alice!
def execute(func, arg):
return func(arg)
print(execute(greet, "Bob")) # 输出:Hello, Bob!
通过上述代码,我们可以看到函数作为对象的灵活性。接下来,我们需要在此基础上构建装饰器的核心逻辑。
高阶函数:装饰器的直接实现基础
高阶函数(Higher-order Function) 是指能够接收函数作为参数,并返回新函数的函数。装饰器本质上就是一种高阶函数,它的核心作用是“包装”原始函数,同时保留原函数的名称和文档信息。
示例 2:第一个装饰器的雏形
def my_decorator(func):
def wrapper(*args, **kwargs):
print("装饰器逻辑开始执行")
result = func(*args, **kwargs)
print("装饰器逻辑执行完毕")
return result
return wrapper
@my_decorator
def say_hello(name):
print(f"你好,{name}!")
say_hello("Charlie")
运行结果:
装饰器逻辑开始执行
你好,Charlie!
装饰器逻辑执行完毕
在上述代码中,@my_decorator
是装饰器的语法糖,其本质等价于:
say_hello = my_decorator(say_hello)
通过这种方式,装饰器 my_decorator
对 say_hello
函数进行了“包装”,在原函数执行前后添加了额外的逻辑。
装饰器的常见使用场景与案例
装饰器的灵活性使其适用于多种场景,例如日志记录、性能计时、权限验证等。以下通过具体案例展示其应用。
案例 1:性能计时器
import time
def timer(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"函数 {func.__name__} 执行耗时:{end_time - start_time:.4f} 秒")
return result
return wrapper
@timer
def compute_sum(n):
return sum(range(n))
compute_sum(1000000)
此装饰器能够自动为函数添加性能统计功能,无需修改原函数代码。
案例 2:权限验证装饰器
def require_login(func):
def wrapper(user, *args, **kwargs):
if user.is_authenticated:
return func(user, *args, **kwargs)
else:
raise PermissionError("用户未登录")
return wrapper
class User:
def __init__(self, is_authenticated):
self.is_authenticated = is_authenticated
@require_login
def access_secure_data(user):
return "敏感数据"
user_logged_in = User(True)
user_guest = User(False)
print(access_secure_data(user_logged_in)) # 成功输出
此装饰器通过检查用户状态,为函数添加了权限验证逻辑。
装饰器的进阶用法:带参数的装饰器
有些场景下,装饰器需要接受额外的参数(例如配置参数或开关选项)。此时可通过“嵌套函数”实现带参数的装饰器。
示例 3:带参数的计时器
def timer_with_threshold(threshold):
def decorator(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
duration = time.time() - start_time
if duration > threshold:
print(f"警告:函数 {func.__name__} 耗时超过 {threshold} 秒!")
return result
return wrapper
return decorator
@timer_with_threshold(0.5)
def slow_function():
time.sleep(1)
slow_function() # 输出警告
通过 @timer_with_threshold(0.5)
,装饰器接收阈值参数,并根据条件触发不同逻辑。
装饰器的注意事项与最佳实践
1. 保留原函数的元数据
当使用装饰器时,默认情况下原函数的 __name__
和 __doc__
属性会被装饰器的 wrapper
函数覆盖。可通过 functools.wraps
解决这一问题:
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# ...
return func(*args, **kwargs)
return wrapper
2. 装饰器的叠加顺序
多个装饰器叠加时,执行顺序遵循“从下至上,从内向外”的原则。例如:
@decorator1
@decorator2
def my_func():
pass
等价于:
my_func = decorator1(decorator2(my_func))
3. 类装饰器的实现
除了函数形式的装饰器,Python 还支持通过类实现装饰器。例如:
class CounterDecorator:
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)
@CounterDecorator
def add(a, b):
return a + b
add(1, 2) # 输出调用次数
装饰器在框架中的实际应用
装饰器在 Python 生态中被广泛使用,例如:
- Flask/Django:路由装饰器(如
@app.route
) - 单元测试:
@pytest.mark.parametrize
- 缓存优化:第三方库
lru_cache
示例 4:模拟框架路由系统
class WebFramework:
def __init__(self):
self.routes = {}
def route(self, path):
def decorator(func):
self.routes[path] = func
return func
return decorator
def dispatch_request(self, path):
view_func = self.routes.get(path)
if view_func:
return view_func()
else:
return "404 Not Found"
app = WebFramework()
@app.route("/home")
def home():
return "欢迎来到首页!"
@app.route("/about")
def about():
return "关于我们页面"
print(app.dispatch_request("/home")) # 输出欢迎信息
此示例展示了如何通过装饰器构建简单的路由系统,模拟了 Web 框架的核心功能。
总结
通过本文的讲解,我们系统学习了“Python 实现一个装饰器函数”的核心原理与实践方法。装饰器通过高阶函数的特性,将代码的“横切关注点”(如日志、权限、性能)与业务逻辑分离,极大提升了代码的清晰度和复用性。
对于开发者而言,掌握装饰器不仅是技术能力的提升,更是 Python 风格的最佳实践。建议读者在实际项目中尝试以下步骤:
- 从简单装饰器开始,逐步增加复杂度;
- 使用
functools.wraps
保留元数据; - 通过注释或文档说明装饰器的功能与参数;
- 结合单元测试验证装饰器的正确性。
通过持续实践,装饰器将成为你代码优化的得力工具,助力构建更优雅、可维护的 Python 程序。