装饰器的完整实现及原理
Posted reklen
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了装饰器的完整实现及原理相关的知识,希望对你有一定的参考价值。
1、简单装饰器
说明:代码在下边。装饰前后,我们都打印一遍如下内容,做一下对比。
- print(foo) # 打印当前函数对象
- print(foo.__name__) # 打印foo函数的函数名
- print(foo.__doc__) # 打印foo函数的文档字符串(docString)
装饰之前:
- <function foo at 0x000002569AAB5620>
- foo
- this is foo
装饰之后:
- <function check_result.<locals>.wrapper at 0x00000250E11F56A8>
- wrapper
- this is wrapper
可以看出,foo的引用发生了变化。装饰的过程是这样的,python解释器会将foo作为参数传递给check_result函数,func就引用了foo函数,并将返回值给了foo,因为返回值wrapper引用了wrapper函数,所以foo的引用就变为了wrapper函数。这时再去执行foo,就等于是在执行wrapper函数了。需要注意的是在装饰之后,我我们的foo函数并没有销毁,而是被func引用着。不然执行的时候也不会有结果了。可是怎么查看呢?使用 print(foo.__closure__[0].cell_contents) 则会看到输出 <function foo at 0x000001F2C7465620> 。这样我就找到了最开始定义的foo函数了!
1 ‘‘‘ 2 简单装饰器: 3 实现装饰后函数的功能是如果x和y相乘为负数,则返回0 4 ‘‘‘ 5 6 def check_result(func): 7 ‘‘‘hahah‘‘‘ 8 def wrapper(*args, **kwargs): 9 ‘‘‘this is wrapper‘‘‘ 10 result = func(*args, **kwargs) 11 12 if result < 0: 13 return 0 14 else: 15 return result 16 return wrapper 17 18 19 @check_result 20 def foo(x, y): 21 ‘‘‘this is foo‘‘‘ 22 return x * y 23 24 25 26 27 # 装饰过程拆解 28 # wrapper = check_result(foo) 29 # foo = wrapper
2、多层装饰器
说明:在理解简单装饰器的基础上,再来看多层的装饰器,会很好理解。只需要关注一下多层装饰器的装饰顺序和执行顺序即可。
1 ‘‘‘ 2 多层装饰器 3 ‘‘‘ 4 5 def deco1(func): 6 def wrapper1(*args, **kwargs): 7 ‘‘‘this is wrapper1‘‘‘ 8 print(‘start 1‘) 9 result = func(*args, **kwargs) 10 print(‘end 1‘) 11 return result 12 return wrapper1 13 14 15 def deco2(func): 16 def wrapper2(*args, **kwargs): 17 ‘‘‘this is wrapper2‘‘‘ 18 print(‘start 2‘) 19 result = func(*args, **kwargs) 20 print(‘end 2‘) 21 return result 22 return wrapper2 23 24 25 def deco3(func): 26 def wrapper3(*args, **kwargs): 27 ‘‘‘this is wrapper3‘‘‘ 28 print(‘start 3‘) 29 result = func(*args, **kwargs) 30 print(‘end 3‘) 31 return result 32 return wrapper3 33 34 35 @deco1 36 @deco2 37 @deco3 38 def foo(x, y): 39 ‘‘‘this is foo‘‘‘ 40 return x * y 41 42 print(foo(8, 9)) 43 ‘‘‘ 44 输出结果: 45 start 1 46 start 2 47 start 3 48 end 3 49 end 2 50 end 1 51 72 52 ‘‘‘ 53 54 55 ‘‘‘ 56 装饰的过程: 57 wrapper3 = deco3(foo) 58 wrapper2 = deco2(wrapper3) 59 wrapper1 = deco1(wrapper2) 60 foo = wrapper1 61 62 63 执行的过程:正好和装饰的过程相反。 64 foo(8, 9)--->wrapper1(8, 9)--->deco1(wrapper2)(8, 9)---> 65 | 66 v 67 deco1( deco2( deco3(foo) ) )(8, 9)<---deco1( deco2(wrapper3) )(8, 9) 68 类比穿衣服,穿(装饰)的时候从里往外一层套一层,脱(执行)的时候从外到里一层一层脱。 69 ‘‘‘
3、带参装饰器
说明:传入函数执行的次数,统计执行完成的时间。
实现方法:使用工厂模式,定义一个工厂函数,它本身不是装饰器,但是返回值是一个装饰器,是用来"生产"装饰器的。如下所示:
1 ‘‘‘ 2 带参装饰器 3 ‘‘‘ 4 5 import time 6 7 def timer(count): 8 def deco(func): 9 def wrapper(*args, **kwargs): 10 ‘‘‘this is wrapper‘‘‘ 11 t1 = time.time() 12 for i in range(count): 13 result = func(*args, **kwargs) 14 t2 = time.time() 15 print(t2 - t1) 16 return result 17 return wrapper 18 return deco 19 20 21 @timer(10000000) # 获取foo执行10000000次的时间 22 def foo(x, y): 23 ‘‘‘this is foo‘‘‘ 24 return x * y
4、类装饰器
说明:想要明白类装饰器的原理,我们先要了解一下__call__这个方法。这个方法是python中所有能被调用的对象具有的内置方法,比如类,函数。调用类的过程就是得到一个类的实例的过程,如果要做类装饰器我们得让该类的实例可以被调用。装饰器的本质也是一个函数被调用执行的过程。先看下面一段代码:
1 class A: 2 pass 3 4 5 def foo(x, y): 6 return x, y 7 8 9 print(A.__call__) 10 print(foo.__call__) 11 12 print(foo.__call__(3, 5)) # 打印结果 15 13 print(foo(3, 5)) # 打印结果 15
打印结果为:可以看到,有__call__方法的才能被调用。还有一个方法判断一个对象是否能被调用 callable() ,传入对象,若返回True则表示能被调用,否则不能被调用。
<method-wrapper ‘__call__‘ of type object at 0x000002CE9522BF48>
<method-wrapper ‘__call__‘ of function object at 0x000002CE953B1E18>
根据这个特性,我们设计我们的类装饰器:
1 ‘‘‘ 2 类装饰器 3 ‘‘‘ 4 5 class Deco: 6 ‘‘‘this is Deco‘‘‘ 7 def __init__(self, func): 8 self.func = func 9 10 def __call__(self, *args, **kwargs): 11 result = self.func(*args, **kwargs) 12 if result < 0: 13 return 0 14 else: 15 return result 16 17 18 @Deco # 装饰后若结果为负数则返回0 19 def foo(x, y): 20 ‘‘‘this is foo‘‘‘ 21 return x * y 22 23 ‘‘‘ 24 装饰过程如下: 25 这个时候foo引用了Deco的一个实例,执行foo()也就是实例调用__call__方法的过程。 26 ‘‘‘ 27 foo = Deco(foo)
以上是关于装饰器的完整实现及原理的主要内容,如果未能解决你的问题,请参考以下文章