装饰器通过函数作为第一类对象实现,定义一个接收函数的装饰器,在其内部定义wrapper函数并添加额外逻辑,最后返回wrapper;使用@语法糖将原函数替换为包装后的函数,从而在不修改原函数代码的情况下增强功能。

装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外的功能,装饰器的返回值也是一个函数对象。 它们经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理等等。
装饰器就像给函数穿上了一件“外衣”,这件“外衣”可以给函数增加额外的能力,但不会改变函数本身。
装饰器是如何实现的?
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_decoratordef 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_decoratordef add(a, b): return a + bprint(add(2, 3))
输出结果:
Before calling the functionAfter calling the function5
装饰器能做什么?除了日志和性能监控
装饰器可以做的远不止日志和性能监控。 它们可以用于:
权限验证: 检查用户是否有权限访问某个函数。缓存: 缓存函数的返回值,避免重复计算。重试机制: 在函数执行失败时自动重试。类型检查: 验证函数参数的类型。信号处理: 在函数执行前后发送信号。路由控制: 在Web框架中,将URL映射到对应的处理函数。
例如,一个简单的缓存装饰器:
import functoolsdef 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@cachedef expensive_operation(n): print(f"Calculating for {n}...") return n * nprint(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@MyDecoratordef 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 functoolsdef 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_decoratordef 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 "" + func(*args, **kwargs) + "" return wrapperdef italic(func): def wrapper(*args, **kwargs): return "" + func(*args, **kwargs) + "" return wrapper@bold@italicdef get_message(message): return messageprint(get_message("Hello"))
在这个例子中,
get_message
函数首先被
italic
装饰器装饰,然后再被
bold
装饰器装饰。 因此,输出结果是
Hello
。
以上就是python中的装饰器是如何工作的_python装饰器工作原理与实例详解的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1370796.html
微信扫一扫
支付宝扫一扫