装饰器的完整实现及原理

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)

原文地址:https://www.cnblogs.com/reklen/p/9534941.html

时间: 2024-11-12 15:49:29

装饰器的完整实现及原理的相关文章

【转】【python】装饰器的原理

写在前面: 在开发OpenStack过程中,经常可以看到代码中的各种注解,自己也去查阅了资料,了解了这是python中的装饰器,因为弱类型的语言可以将函数当成返回值返回,这就是装饰器的原理. 虽然说知道装饰器的使用方法以及原理,但是一直不明白为什么要通过在内部函数返回一个函数名这样的写法,在微信上看到下面这篇文章,豁然开朗.因为觉得写的非常好,所以我也没必要再来写一遍了,直接转载,供以后的开发中参考. -----------------------------------------------

【转】详解Python的装饰器

原文链接:http://python.jobbole.com/86717/ Python中的装饰器是你进入Python大门的一道坎,不管你跨不跨过去它都在那里. 为什么需要装饰器 我们假设你的程序实现了say_hello()和say_goodbye()两个函数. def say_hello(): print "hello!" def say_goodbye(): print "hello!" # bug here if __name__ == '__main__':

详解Python的装饰器

Python中的装饰器是你进入Python大门的一道坎,不管你跨不跨过去它都在那里. 为什么需要装饰器 我们假设你的程序实现了say_hello()和say_goodbye()两个函数. def say_hello(): print "hello!" def say_goodbye(): print "hello!" # bug here if __name__ == '__main__': say_hello() say_goodbye() 但是在实际调用中,我们

Python学习之装饰器进阶

函数知识回顾: 函数的参数分为:实参和形参. 实参:调用函数的时候传入的参数: 形参:分为3种(位置参数.默认参数.动态传参) 位置参数:必须传值 def aaa(a,b): print(a,b) aaa(1,2) 默认参数:不是必须传参数 def bbb(x=10): print(x) # bbb() #x = 10 # bbb(20) #x = 20 动态传参 def ccc(*args):#1,2,3,4,5 print(args) ccc(1,2,3,4,5)#按位置传参数 t = (1

Python基础之装饰器

装饰器 1.普通函数 #简单的函数和调用 def a1(): print("i am zhangsan") def a2(): print("i am lisi") a1() a2() 2.在函数前后添加功能 def inner(func): print("添加1") func() print("添加2") return func def a1(): print("i am zhangsan") def a

python闭包及装饰器

一.闭包 1.闭包就是在函数内部定义函数并返回内部函数 2.闭包实现代码的封装和复用 3.实例如图所示,内部定义一个比较函数,给定边界值即做出不同的判断 . 二.装饰器 1.装饰器就是利用闭包的原理 [email protected]就是装饰器的语法糖 3.装饰器可以给函数添加功能 4.实例如图所示,给求和函数添加参数预处理的功能

Python自学之路——装饰器的秘密

先不管装饰器到底是个什么东东,让我们来聊一聊函数的几个点吧.我们知道,在python里,一切皆是对象,那么函数呢,当然也是对象,而且人家还是一级对象呐.既然是对象,那它就可以被赋值给变量,反之,通过变量也能调用函数.好,需特别注意的点来了,函数调用一定是函数名 + (),如果没有这个括号,函数是不会被调用的,它只能是表示内存里的一个地址,看下面 1 def happy(): 2 print('be happy') 3 print (happy) #并没有调用happy函数,只是打印了happy的

python3之装饰器

1.装饰器 装饰器本质上是一个python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象.它经常用于有切面需求的场景,比如:插入日志.性能测试.事务处理.缓存.权限验证等场景,装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用.概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能. 装饰器的实现原理: #无参数装饰器原理: def login(funct): print('passed us

python 基础篇 11 函数进阶----装饰器

11. 前??能-装饰器初识本节主要内容:1. 函数名的运?, 第?类对象2. 闭包3. 装饰器初识 一:函数名的运用: 函数名是一个变量,但他是一个特殊变量,加上括号可以执行函数. ?. 闭包什么是闭包? 闭包就是内层函数, 对外层函数(非全局)的变量的引?. 叫闭包 可以使用_clesure_检测函数是否是闭包  返回cell则是闭包,返回None则不是 闭包的好处: 由它我们可以引出闭包的好处. 由于我们在外界可以访问内部函数. 那这个时候内部函数访问的时间和时机就不?定了, 因为在外部,