Python笔记:装饰器
装饰器
1、特点:装饰器的作用就是为已存在的对象添加额外的功能,特点在于不用改变原先的代码即可扩展功能。
2、使用:装饰器其实也是一个函数,加上@符号后放在另一个函数“头上”就实现了装饰的功能,执行被装饰的函数时,其实相当于func(*args, **kwargs) = decorator(func)(*args, **kwargs)。
3、应用场景:常用的有以下一些场景:
-
- 附加功能。
- 请求拦截:比如登录验证、权限验证等。
- 修改返回值。
- 函数注册。
4、普通装饰器的基本原理:在加载装饰器时,也就是加载到@符时,会运行一次装饰器(也就是被@后的函数),它的返回值会替代被装饰的函数地址,而被装饰的函数的地址以装饰器函数参数的形式传进了装饰器。如下示例中:加载到@decorator时,运行了一次decorator()函数,这时函数hello()把函数名(也就是函数地址hello)作为装饰器参数func传了进去,decorator()执行完后返回函数inner()的函数地址,这个函数地址替代了函数hello()的函数地址,当执行hello('Jom')时,就会执行替换后的函数,即inner('Jom'),然后就会去执行inner()函数内的内容。
普通装饰器示例:
1 def decorator(func): 2 print('hello python!') # 相当于给hello添加的额外功能 3 def inner(name): 4 print('hello, my friend') # 相当于给hello添加的额外功能 5 func(name) # func即为hello()函数 6 7 return inner 8 9 @decorator # 装饰器用@表示,函数decorator()装饰函数hello() 10 def hello(name): 11 print('hello %s!' % name) 12 13 hello('Jom') # 执行函数 14 15 # 输出-------------------------------------------------------------- 16 # hello python! 17 # hello, my friend 18 # hello Jom!
5、装饰器传参:此时定义装饰器时,需要在普通装饰器的外面再定义一个函数,而最外面的这个函数的参数就对应于装饰器使用时传入的参数,具体见示例。
装饰器传参示例:
1 def decorator_var(var1, var2): 2 print('decorator_var') 3 4 # 装饰器的传参,就是在简单装饰器外面再套一层“壳子” 5 # 并且加载到装饰器的时候,会自动执行decorator_var和decorator两个函数 6 def decorator(func): 7 print('decorator') 8 9 def inner(name): 10 print(var1, var2) 11 func(name) 12 13 return inner 14 15 return decorator 16 17 18 @decorator_var('Python', '36') 19 def hello(name): 20 print('hello %s!' % name) 21 22 23 hello('Jom') 24 25 # 输出-------------------------------------------------- 26 # decorator_var 27 # decorator 28 # Python 36 29 # hello Jom!
6、安全防护:被装饰的函数的一些元信息,比如__name__属性,会被修改为装饰器函数返回的函数名(虽然本质上已经不是在执行原函数了,因为它被装饰了,但是“表面上”我们代码还是执行的是这个函数,所以看起来就是原先的函数的属性被修改了,这样“看起来”就是不安全的),但是为了防止这种不安全的行为,可以使用“from functools import wraps”来装饰器装饰器返回的函数,此装饰器会将原函数的一些元信息(如__module__、__name__、__doc__等)拷贝至返回的内函数中。
1 from functools import wraps 2 3 4 # 装饰器__name__属性防护 5 def decorator(func): 6 @wraps(func) # 这个装饰器可以防止被装饰的函数的__name__属性值被修改 7 def inner(*args, **kwargs): # 可以使用*args和**kwargs为“万能”的传参方式,当然这里也可以写成inner(a, b) 8 func(*args, **kwargs) 9 10 return inner 11 12 13 # 加载到这个装饰器时,相当于:test_func = decorator(test_func),即test_func = inner 14 # 如果没有对__name__的保护,加载装饰器后,如:函数test_func的__name__属性就会由“test_func”变为“inner”了 15 @decorator 16 def test_func(a, b): 17 print('sum of a+b: %s' % (a + b))
7、多个装饰器的执行顺序:如果某个函数使用了多个装饰器,那么执行的顺序相对于原函数的位置按照从下到上(从近到远),然后再从上到下(从远到近)的顺序执行,具体见示例。
def decorator1(func): print('decorator1...') def inner(*args, **kwargs): print('decorator1 inner...') func() return inner def decorator2(func): print('decorator2...') def inner(*args, **kwargs): print('decorator2 inner...') func() return inner def decorator3(func): print('decorator3...') def inner(*args, **kwargs): print('decorator3 inner...') func() return inner # 多个装饰器,对于装饰器函数中、内函数外的部分按照从下到上的顺序执行, # 对于内函数中的部分则按照从上到下的顺序执行。 @decorator3 @decorator2 @decorator1 def test_func(): print('test_func...') if __name__ == '__main__': test_func() # 输出-------------------------------- # decorator1... # decorator2... # decorator3... # decorator3 inner... # decorator2 inner... # decorator1 inner... # test_func...
8、类装饰器:如果想要把一个装饰器定义为一个类,其实原理都是一样的,将装饰器当成一个可调用的对象即可,第一次加载到@Decorator时,会执行Decorator(func)语句,而执行原函数时,就是相当于执行Decorator(func)(*args, **kwargs),具体见示例。
普通类装饰器:
class Decorator: def __init__(self, func): print('__init__...') self._func = func def __call__(self, *args, **kwargs): print('__call__...') self._func(*args, **kwargs) @Decorator def test_func(): print('test_func...') if __name__ == '__main__': test_func() # 输出------------------------ # __init__... # __call__... # test_func...
类装饰器传参示例:
class Decorator: def __init__(self, string): self._func = None print('__init__...') print(string) def decorator_func(self): print('decorator_func...') self._func() def __call__(self, *args, **kwargs): print('__call__...') self._func = args[0] return self.decorator_func @Decorator('hello') def test_func(): print('test_func...') if __name__ == '__main__': test_func() # 输出------------------------ # __init__... # hello # __call__... # decorator_func... # test_func...
总结:由上面的示例其实可以看出,在定义装饰器时,无论是定义为函数的形式还是类的形式,使用时只需要简单记为decorator(func)(*args, **kwargs)就可以了,如果需要给装饰器传参,不过是在前面多了一个执行步骤而已,就变为了decorator(*dec_args, **dec_kwargs)(func)(*args, **kwargs),dec_args和dec_kwargs是给装饰器传入的参数。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律