装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外的功能,装饰器的返回值也是一个函数对象。 它们经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理等等。
装饰器就像给函数穿上了一件“外衣”,这件“外衣”可以给函数增加额外的能力,但不会改变函数本身。
装饰器是如何实现的?Python的装饰器是基于函数是“第一类对象”这一概念实现的。这意味着函数可以像任何其他对象一样被传递、赋值和作为返回值。
简单来说,装饰器通过以下步骤工作:
- 定义一个装饰器函数,该函数接收一个函数作为参数。
- 在装饰器函数内部,定义一个新的函数(通常称为wrapper函数)。
- wrapper函数中,调用原始函数,并在调用前后添加额外的功能。
- 装饰器函数返回wrapper函数。
当使用
@decorator语法糖来装饰一个函数时,实际上是将原始函数作为参数传递给装饰器函数,并将装饰器返回的wrapper函数赋值给原始函数的名字。
def my_decorator(func): def wrapper(): print("Something is happening before the function is called.") func() print("Something is happening after the function is called.") return wrapper @my_decorator def say_hello(): print("Hello!") say_hello()
这段代码的执行结果是:
Something is happening before the function is called. Hello! Something is happening after the function is called.如何处理带参数的函数?
如果被装饰的函数带有参数,wrapper函数需要能够接收和传递这些参数。可以使用
*args和
**kwargs来实现:
def my_decorator(func): def wrapper(*args, **kwargs): print("Before calling the function") result = func(*args, **kwargs) print("After calling the function") return result return wrapper @my_decorator def add(a, b): return a + b print(add(2, 3))
输出结果:
Before calling the function After calling the function 5装饰器能做什么?除了日志和性能监控
装饰器可以做的远不止日志和性能监控。 它们可以用于:
- 权限验证: 检查用户是否有权限访问某个函数。
- 缓存: 缓存函数的返回值,避免重复计算。
- 重试机制: 在函数执行失败时自动重试。
- 类型检查: 验证函数参数的类型。
- 信号处理: 在函数执行前后发送信号。
- 路由控制: 在Web框架中,将URL映射到对应的处理函数。
例如,一个简单的缓存装饰器:

全面的AI聚合平台,一站式访问所有顶级AI模型


import functools def cache(func): cached_values = {} @functools.wraps(func) def wrapper(*args): if args in cached_values: return cached_values[args] else: result = func(*args) cached_values[args] = result return result return wrapper @cache def expensive_operation(n): print(f"Calculating for {n}...") return n * n print(expensive_operation(5)) print(expensive_operation(5)) # 从缓存中获取装饰器与类装饰器有什么区别?
除了函数装饰器,Python还支持类装饰器。 类装饰器接收一个函数或类作为参数,并返回一个修改后的函数或类。
类装饰器通常通过实现
__call__方法来工作。 当使用类装饰器装饰一个函数时,实际上是创建了该类的一个实例,并将被装饰的函数作为参数传递给类的构造函数。 当调用被装饰的函数时,实际上是调用了类实例的
__call__方法。
class MyDecorator: def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): print("Before calling the function") result = self.func(*args, **kwargs) print("After calling the function") return result @MyDecorator def say_hello(name): print(f"Hello, {name}!") say_hello("Alice")如何编写一个带参数的装饰器?
有时候,我们需要一个可以接收参数的装饰器。 这可以通过创建一个返回装饰器函数的函数来实现。
def repeat(num_times): def decorator_repeat(func): def wrapper(*args, **kwargs): for _ in range(num_times): result = func(*args, **kwargs) return result return wrapper return decorator_repeat @repeat(num_times=3) def greet(name): print(f"Hello, {name}!") greet("Bob")
这个例子中,
repeat函数接收一个
num_times参数,并返回一个装饰器函数
decorator_repeat。
decorator_repeat函数再接收被装饰的函数
func,并返回wrapper函数。 装饰器会影响函数的元数据吗?
默认情况下,装饰器会改变被装饰函数的元数据,例如
__name__和
__doc__。 这可能会导致一些问题,例如在使用
help()函数时显示不正确的信息。
为了解决这个问题,可以使用
functools.wraps装饰器来保留原始函数的元数据。
import functools def my_decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): """This is the wrapper function.""" print("Before calling the function") result = func(*args, **kwargs) print("After calling the function") return result return wrapper @my_decorator def say_hello(): """This is the original function.""" print("Hello!") print(say_hello.__name__) print(say_hello.__doc__)
使用了
functools.wraps后,
say_hello.__name__会输出
say_hello,
say_hello.__doc__会输出
This is the original function.。 如果没有使用
functools.wraps,则会输出wrapper函数的信息。 装饰器链:多个装饰器叠加使用
可以同时使用多个装饰器来增强一个函数的功能,这就是装饰器链。 装饰器的应用顺序是从下往上,从里到外。
def bold(func): def wrapper(*args, **kwargs): return "<b>" + func(*args, **kwargs) + "</b>" return wrapper def italic(func): def wrapper(*args, **kwargs): return "<i>" + func(*args, **kwargs) + "</i>" return wrapper @bold @italic def get_message(message): return message print(get_message("Hello"))
在这个例子中,
get_message函数首先被
italic装饰器装饰,然后再被
bold装饰器装饰。 因此,输出结果是
<b><i>Hello</i></b>。
以上就是python中的装饰器是如何工作的_python装饰器工作原理与实例详解的详细内容,更多请关注知识资源分享宝库其它相关文章!
相关标签: python app 路由 性能测试 区别 python函数 权限验证 Python 构造函数 function 对象 this 大家都在看: python怎么检查一个键是否存在于字典中_python字典键存在性检查 Python怎么实现一个上下文管理器_Python上下文管理器协议实现 python中怎么给函数设置默认参数_Python函数默认参数设置方法 python中怎么测量一段代码的执行时间? python怎么创建一个虚拟环境_python虚拟环境创建与使用教程
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。