装饰器

定义

给已有函数增加额外功能的函数。其本质上就是一个闭包函数。

特点

1. 不修改已有函数的源代码(无风险)

2. 不修改已有函数的调用方式(无风险)

3. 给已有函数增加额外的功能(只需要关注额外增加的部分)

语法

1. 定义一个装饰器

2. 在目标函数上使用装饰器

通过@装饰器名称 放在定义目标函数上一行,就完成对已有函数的装饰操作

 

示例

复制代码
 1 '''
 2 定义: 给已有函数增加额外功能的函数。其本质上就是一个闭包函数。
 3 
 4 特点
 5     1. 不修改已有函数的源代码(无风险)
 6     2. 不修改已有函数的调用方式(无风险)
 7     3. 给已有函数增加额外的功能(只需要关注额外增加的部分)
 8 '''
 9 
10 
11 # 1. 定义一个装饰器,本质上就是闭包
12 def decorator(func):
13     def wrapper(*args, **kwargs):
14         print("Before function execution")
15         result = func(*args, **kwargs)
16         print("After function execution")
17         return result
18 
19     return wrapper
20 
21 
22 # 2. 使用装饰器
23 @decorator
24 def my_function():
25     print("Inside my_function")
26 
27 
28 '''
29 3. 执行被装饰方法,验证装饰器是否生效
30 输出: 
31     Before function execution
32     Inside my_function
33     After function execution
34 
35 '''
36 my_function()
复制代码

这个示例中,decorator是一个装饰器函数,它接受一个函数作为参数,返回一个新的函数wrapper。wrapper函数会在被装饰的函数my_function执行前后打印一些信息。最后,通过使用@decorator语法将decorator应用到my_function上。

 

 

装饰器的流程图

装饰器本质上是闭包。只不过要求外部函数的参数必须是函数,实参是目标函数的引用。内部函数内部必须调用目标函数,内部函数形参列表要包含目标函数的形参列表,如果闭包函数有且只有一个参数,那必须是函数类型,这样定义的函数才是装饰器。。

实际执行过程如下:

1. 先按要求定义一个闭包

2. 以目标函数为参数,调用外部函数得到闭包函数即内部函数的引用

3. 调用闭包函数即内部函数。内部函数会在执行目标函数前后增加扩展功能。这个扩展功能就是对目标函数的装饰。类似于java装的AOP切面

 

使用场景

装饰器可以用于很多场景,例如记录函数执行时间、缓存函数结果、验证用户权限等。下面是几个常见的装饰器示例:

 

1. 记录函数执行时间的装饰器

复制代码
 1 import time
 2 
 3 
 4 def timer(func):
 5     def wrapper(*args, **kwargs):
 6         start_time = time.time()
 7         result = func(*args, **kwargs)
 8         end_time = time.time()
 9         execution_time = end_time - start_time
10         print("Execution time: {} seconds".format(execution_time))
11         return result
12 
13     return wrapper
14 
15 
16 @timer
17 def my_function():
18     time.sleep(1)
19 
20 
21 my_function()
复制代码

 2. 缓存函数结果的装饰器

复制代码
 1 from functools import lru_cache
 2 
 3 
 4 @lru_cache(maxsize=None)
 5 def fibonacci(n):
 6     if n <= 1:
 7         return n
 8     else:
 9         return fibonacci(n - 1) + fibonacci(n - 2)
10 
11 
12 print(fibonacci(10))
复制代码

这段代码使用了Python标准库中的functools模块中的lru_cache装饰器来优化斐波那契数列的计算。让我逐步解释这段代码的每个部分:

  1. 首先,代码导入了functools模块中的lru_cache装饰器。

  2. @lru_cache(maxsize=None)这行是一个装饰器,应用在fibonacci函数上。lru_cache是"Least Recently Used Cache"(最近最少使用缓存)的缩写,它是一种用于缓存函数调用结果的方法。maxsize=None表示缓存的大小没有限制,可以存储所有不同参数的函数调用结果。

  3. def fibonacci(n): 声明了一个名为fibonacci的函数,它接受一个整数参数n,代表斐波那契数列的索引。

  4. 在函数内部,通过递归来计算斐波那契数列的第n个数。如果n小于等于1,直接返回n作为结果。

  5. 如果n大于1,递归地调用fibonacci(n - 1)fibonacci(n - 2),然后将它们的结果相加,从而计算出第n个斐波那契数。

  6. 在函数内部的递归调用中,由于应用了lru_cache装饰器,先前计算过的结果会被缓存起来,以避免重复计算。

  7. 最后,print(fibonacci(10))调用了fibonacci函数并打印 [Cannot read properties of undefined (reading 'status')]

3. 验证用户权限的装饰器

复制代码
 1 def require_permission(permission):
 2     def decorator(func):
 3         def wrapper(*args, **kwargs):
 4             # 检查用户权限
 5             if has_permission(permission):
 6             # if permission == "admin":
 7                 return func(*args, **kwargs)
 8             else:
 9                 raise PermissionError("You don't have permission to access this resource")
10 
11         return wrapper
12 
13     return decorator
14 
15 
16 @require_permission("admin")
17 def delete_user(user_id):
18     # 模拟删除用户
19     pass
20 
21 
22 def has_permission(permission):
23     print(permission)
24     return False  # 模拟没有权限
25 
26 
27 delete_user(123)
复制代码

多个装饰器执行顺序

多个装饰器的执行顺序是从内到外依次应用的,即最靠近被装饰函数的装饰器首先执行,然后依次向外执行。

让我通过一个示例来详细解释这个过程。

假设有两个装饰器:decorator1decorator2,以及一个被装饰的函数 my_function。它们的定义如下:

复制代码
 1 def decorator1(func):
 2     def wrapper(*args, **kwargs):
 3         print("Decorator 1: Before function call")
 4         result = func(*args, **kwargs)
 5         print("Decorator 1: After function call")
 6         return result
 7 
 8     return wrapper
 9 
10 
11 def decorator2(func):
12     def wrapper(*args, **kwargs):
13         print("Decorator 2: Before function call")
14         result = func(*args, **kwargs)
15         print("Decorator 2: After function call")
16         return result
17 
18     return wrapper
19 
20 
21 @decorator1
22 @decorator2
23 def my_function():
24     print("my_function is called")
25 
26 
27 '''
28 输出:
29     Decorator 1: Before function call
30     Decorator 2: Before function call
31     my_function is called
32     Decorator 2: After function call
33     Decorator 1: After function call
34 '''
35 my_function()
复制代码

现在,让我们逐步解释代码和装饰器的执行顺序:

  1. 首先,my_function被传递给 decorator2 装饰器,相当于 decorator2(my_function)。在 decorator2 内部,它的 wrapper 函数包装了 my_function

  2. my_function 被调用时,实际上是调用了 decorator2 内部的 wrapper 函数。这将输出 "Decorator 2: Before function call",然后调用原始的 my_function

  3. decorator2wrapper 函数内部,my_function 被调用,输出 "my_function is called"。

  4. 调用完成后,decorator2wrapper 函数继续执行,输出 "Decorator 2: After function call",然后将控制权返回给 decorator1

  5. 现在,decorator1 被应用于已经被 decorator2 包装过的 my_function(即 decorator1(wrapper(my_function)))。

  6. decorator1wrapper 函数内部,输出 "Decorator 1: Before function call",然后调用已经被 decorator2decorator1 包装过的函数。

  7. 同样地,输出 "my_function is called"。

  8. 调用完成后,decorator1wrapper 函数继续执行,输出 "Decorator 1: After function call"。

综上所述,装饰器的执行顺序是从最靠近被装饰函数的装饰器开始,然后逐步向外执行。在示例中,先执行 decorator2,再执行 decorator1

调用 my_function() 会产生以下输出:

1
2
3
4
5
Decorator 1: Before function call
Decorator 2: Before function call
my_function is called
Decorator 2: After function call
Decorator 1: After function call

  

总结:

1. 离被装饰函数最近的装饰器先装饰

2. 然后外面的装饰器再进行装饰(此时是已经装饰的函数)

3. 总的顺序为   内 -->  外,进行层层加工、装饰

posted @   Allen_Hao  阅读(16)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能
点击右上角即可分享
微信分享提示