Python 装饰器的形成过程

装饰器
  定义:本质是函数,(装饰其他函数),即为其他函数添加附加功能。
  原则: 1、不能修改被装饰的函数的源代码;
            2、不能修改被装饰的函数的调用方式。

实现装饰器知识储备:
   1. 函数即‘变量‘        
   2. 高阶函数
       a. 把一个函数名当作实参传递给另一个函数(在不修改被装饰函数源代码的前提下为其添加新功能)
       b. 返回值中包含函数名(不修改函数的调用方式)
   3. 嵌套函数

高阶函数 + 嵌套函数 (组成)--> 装饰器

1、必备

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: antcolonies

‘‘‘第一波‘‘‘

def foo():
    print(‘in the foo‘)

foo               # 函数名,相当于变量
print(foo)        # <function foo at 0x0000000001CFCBF8>  变量的索引地址
foo()             # in the foo    执行/调用函数foo

‘‘‘第二波‘‘‘

def foo(x):
    print(‘in the foo_%d‘ %x)

foo = lambda x:x+1

print(foo)

print(foo(1))     # 执行下面的lambda表达式,而不再是原来的foo函数,因为函数 foo 被重新定义了

‘‘‘
<function foo at 0x0000000001CFCBF8>
in the foo
<function <lambda> at 0x0000000001CFCBF8>
2
‘‘‘

2、需求来了

初创公司有N个业务部门,1个基础平台部门,基础平台负责提供底层的功能,如:数据库操作、redis调用、监控API等功能。业务部门使用基础功能时,只需调用基础平台提供的功能即可。如下:

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 # Author: antcolonies
 4
 5 ############### 基础平台提供的功能如下 ###############
 6
 7 def f1():
 8     print(‘in the f1‘)
 9
10 def f2():
11     print(‘in the f2‘)
12
13 def f3():
14     print(‘in the f3‘)
15
16 def f4():
17     print(‘in the f4‘)
18
19 ############### 业务部门A 调用基础平台提供的功能 ###############
20
21 f1()
22 f2()
23 f3()
24 f4()
25
26 ############### 业务部门B 调用基础平台提供的功能 ###############
27
28 f1()
29 f2()
30 f3()
31 f4()

function call

目前公司有条不紊的进行着,但是,以前基础平台的开发人员在写代码时候没有关注验证相关的问题,即:基础平台的提供的功能可以被任何人使用。现在需要对基础平台的所有功能进行重构,为平台提供的所有功能添加验证机制,即:执行功能前,先进行验证。

老大把工作交给 Low B,他是这么做的:

跟每个业务部门交涉,每个业务部门自己写代码,调用基础平台的功能之前先验证。诶,这样一来基础平台就不需要做任何修改了。

当天Low B 被开除了...

老大把工作交给 Low BB,他是这么做的:

只对基础平台的代码进行重构,让N业务部门无需做任何修改。

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 # Author: antcolonies
 4
 5 ############### 基础平台提供的功能如下 ###############
 6
 7 def f1():
 8     # 验证1
 9     # 验证2
10     # 验证3
11     print(‘in the f1‘)
12
13 def f2():
14     # 验证1
15     # 验证2
16     # 验证3
17     print(‘in the f2‘)
18
19 def f3():
20     # 验证1
21     # 验证2
22     # 验证3
23     print(‘in the f3‘)
24
25 def f4():
26     # 验证1
27     # 验证2
28     # 验证3
29     print(‘in the f4‘)
30
31 ############### 业务部门不变 ###############
32 ### 业务部门A 调用基础平台提供的功能###
33
34 f1()
35 f2()
36 f3()
37 f4()
38
39 ### 业务部门B 调用基础平台提供的功能 ###
40
41 f1()
42 f2()
43 f3()
44 f4()

authoration

过了一周 Low BB 被开除了...

老大把工作交给 Low BBB,他是这么做的:

只对基础平台的代码进行重构,其他业务部门无需做任何修改

 1 ############### 基础平台提供的功能如下 ###############
 2
 3 def check_login():
 4     # 验证1
 5     # 验证2
 6     # 验证3
 7     pass
 8
 9
10 def f1():
11     check_login()
12     print ‘f1‘
13
14 def f2():
15     check_login()
16     print ‘f2‘
17
18 def f3():
19     check_login()
20     print ‘f3‘
21
22 def f4():
23     check_login()
24     print ‘f4‘

authoration_1

老大看了下Low BBB 的实现,嘴角漏出了一丝的欣慰的笑,语重心长的跟Low BBB聊了个天:

老大说:

写代码要遵循开发封闭原则,虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即:

  • 封闭:已实现的功能代码块
  • 开放:对扩展开发

如果将开放封闭原则应用在上述需求中,那么就不允许在函数 f1 、f2、f3、f4的内部进行修改代码,老板就给了Low BBB一个实现方案:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: antcolonies

def w1(func):
    def inner():
        # 验证1
        # 验证2
        # 验证3
        return func()
    return inner

@w1
def f1():
    print(‘in the f1‘)

@w1
def f2():
    print(‘in the f2‘)

@w1
def f3():
    print(‘in the f3‘)

@w1
def f4():
    print(‘in the f4‘)

对于上述代码,也是仅仅对基础平台的代码进行修改,就可以实现在其他人调用函数 f1 f2 f3 f4 之前都进行【验证】操作,并且其他业务部门无需做任何操作。

Low BBB心惊胆战的问了下,这段代码的内部执行原理是什么呢?

老大正要生气,突然Low BBB的手机掉到地上,恰恰屏保就是Low BBB的女友照片,老大一看一紧一抖,喜笑颜开,交定了Low BBB这个朋友。详细的开始讲解了:

以下面为例:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: antcolonies

import time

def timer(func):
    def deco():
        start_time = time.time()
        func()
        stop_time = time.time()
        print(‘the func run time is %s‘ %(stop_time - start_time))
    return deco

@timer        # test1 = timer(test1)
def test1():
    time.sleep(3)
    print(‘in the test1‘)

test1()

下面解析一下以上代码的执行(解释器解释)过程:

1. 解释 import time

2. 初始化 def timer(func):  ,即将函数变量timer和形参入栈内存

3. 解释 第5行和第6行代码,因为@timer;def test1(): 是一个整体结构(语法糖),相当于第2步的初始化,解释的结果是: test1 = timer(test1)

4. 执行test1 = timer(test1),注意此时timer(test1)中的test1是def test1(): 中的函数名,存储于栈内存,timer(test1)将函数名test1作为实参传入timer()函数

5. 接着初始化def deco(): 入栈内存,并将函数名deco以返回值的形式赋值给第3步中的test1 = timer(test1),故此时test1的值为函数deco的函数体的内存地址

6. 接着至第20行解析test1(),由于此时test1的值为deco的内存地址,故接下来会调用函数deco

7. 依次执行函数deco的函数体start_time = time.time(),func()

8. 由于第3步中有实参传入,故此时的形参func即为test1, 即执行test1(),调用函数def test1(): 并执行之

9. 接着执行第11和12行。整个程序模块执行完毕。

执行结果:

in the test1
the func run time is 9.18652606010437(debug模式下得出的结果)

先把上述流程看懂,之后还会继续更新...

3、问答时间

问题:当被装饰(或扩充)的函数有参数,而且参数个数不一致时:

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 # Author: antcolonies
 4
 5 import time
 6
 7 def timer(func):
 8     def deco(argv1,argv2):
 9         start_time = time.time()
10         func(argv1,argv2)
11         stop_time = time.time()
12         print(‘the func run time is %s‘ %(stop_time - start_time))
13     return deco
14
15 @timer
16 def test2(name, age):
17     time.sleep(2)
18     print(‘in the test2: %s %d‘ %(name, age))
19
20 test2(‘alex‘,22)
21
22 ‘‘‘
23 in the test2: alex 22
24 the func run time is 2.000114917755127
25 ‘‘‘

有参数

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 # Author: antcolonies
 4
 5 import time
 6
 7 def timer(func):
 8     def deco():
 9         start_time = time.time()
10         func()
11         stop_time = time.time()
12         print(‘the func run time is %s‘ %(stop_time - start_time))
13     return deco
14
15 @timer        # test1 = timer(test1)
16 def test1():
17     time.sleep(2)
18     print(‘in the test1‘)
19
20 @timer
21 def test2(name, age):
22     time.sleep(2)
23     print(‘in the test2: %s %d‘ %(name, age))
24
25 test1()
26 test2(‘alex‘,22)
27
28 ‘‘‘
29 D:\Python\python.exe E:/python14_workspace/s14/day04/decorator_4_2.py
30 in the test1
31 the func run time is 2.0001139640808105
32 Traceback (most recent call last):
33   File "E:/python14_workspace/s14/day04/decorator_4_2.py", line 26, in <module>
34     test2(‘alex‘,22)
35 TypeError: deco() takes 0 positional arguments but 2 were given
36
37 Process finished with exit code 1
38 ‘‘‘

参数不固定

发现当参数不固定时,会出现错误,故需将其改进:

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 # Author: antcolonies
 4
 5 import time
 6
 7 def timer(func):
 8     def deco(*argv):
 9         start_time = time.time()
10         func(*argv)
11         stop_time = time.time()
12         print(‘the func run time is %s‘ %(stop_time - start_time))
13     return deco
14
15 @timer        # test1 = timer(test1)
16 def test1():
17     time.sleep(2)
18     print(‘in the test1‘)
19
20 @timer
21 def test2(name, age):
22     time.sleep(2)
23     print(‘in the test2: %s %d‘ %(name, age))
24
25 test1()
26 test2(‘alex‘,22)
27
28 ‘‘‘
29 in the test1
30 the func run time is 2.000114917755127
31 in the test2: alex 22
32 the func run time is 2.0001139640808105
33 ‘‘‘

不固定参数形式_ *args

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 # Author: antcolonies
 4
 5 import time
 6
 7 def timer(func):
 8     def deco(*args,**kwargs):
 9         start_time = time.time()
10         func(*args,**kwargs)
11         stop_time = time.time()
12         print(‘the func run time is %s‘ %(stop_time - start_time))
13     return deco
14
15 @timer        # test1 = timer(test1)
16 def test1():
17     time.sleep(2)
18     print(‘in the test1‘)
19
20 @timer
21 def test2(name, age):
22     time.sleep(2)
23     print(‘in the test2: %s %d‘ %(name, age))
24
25 test1()
26 test2(‘alex‘,age = 22)   # 有关键参数时
27
28 ‘‘‘
29 in the test1
30 the func run time is 2.0001139640808105
31 in the test2: alex 22
32 the func run time is 2.000114917755127
33 ‘‘‘

有关键参数形式_ *args,**kwargs

装饰器应用实例:

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 # Author: antcolonies
 4
 5 usr,passwd = ‘alex‘,‘abc123‘
 6
 7 def auth(func):
 8     def wrapper(*args, **kwargs):
 9         username = input("username:").strip()
10         password = input("password:").strip()
11         if username == usr and password == passwd:
12             print(‘\033[32;1mUser has passed authentication.\033[0m‘)
13             return func(*args,**kwargs)
14         else:
15             exit(‘\033[31;1mInvalid username or password.\033[0m‘)
16     return wrapper
17
18
19 def index():
20     print(‘welcome to index page.‘)
21
22 @auth
23 def home():
24     print(‘welcome to home page.‘)
25     return ‘from home‘
26
27 @auth
28 def bbs():
29     print(‘welcome to bbs page.‘)
30
31 index()
32 print(home())
33 bbs()

论坛

出现以上的当登录home()和bbs模块时,需要通过验证。但是当不同的登录渠道需要不同的验证处理时,以上模块代码就傻眼了。故针对不同的登录渠道需要个性化的验证:

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 # Author: antcolonies
 4
 5 usr,passwd = ‘alex‘,‘abc123‘
 6
 7 def auth(auth_type):                   # 增加个性化定制参数
 8     print(‘auth func:‘, auth_type)
 9     def outer_wrapper(func):
10         def wrapper(*args, **kwargs):
11             print(‘auth func args:‘, *args, **kwargs)
12             if auth_type == ‘local‘:
13                 username = input("username:").strip()
14                 password = input("password:").strip()
15                 if username == usr and password == passwd:
16                     print(‘\033[32;1mUser has passed authentication.\033[0m‘)
17                     return func(*args, **kwargs)
18                 else:
19                     exit(‘\033[31;1mInvalid username or password.\033[0m‘)
20             elif auth_type == ‘ldap‘:
21                 print(‘get hell out here!‘)
22         return wrapper
23     return outer_wrapper
24
25
26 def index():
27     print(‘welcome to index page.‘)
28
29 @auth(auth_type = ‘local‘)         # 个性化定制
30 def home():
31     print(‘welcome to home page.‘)
32     return ‘from home‘
33
34 @auth(auth_type = ‘ldap‘)
35 def bbs():
36     print(‘welcome to bbs page.‘)
37
38 index()
39 print(home())
40 bbs()

个性化验证

4、functools.wraps

上述的装饰器虽然已经完成了其应有的功能,即:装饰器内的函数代指了原函数,注意其只是代指而非相等,原函数的元信息没有被赋值到装饰器函数内部。例如:函数的注释信息。

 1 def outer(func):
 2     def inner(*args, **kwargs):
 3         print(inner.__doc__)  # None
 4         return func()
 5     return inner
 6
 7 @outer
 8 def function():
 9     """
10     asdfasd
11     :return:
12     """
13     print(‘func‘)

无元信息

如果使用functools模块中的@functools.wraps装饰的装饰器内的函数,那么就会代指元信息和函数。

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 # Author: antcolonies
 4
 5 import functools
 6
 7 def outer(func):
 8     @functools.wraps(func)
 9     def inner(*args, **kwargs):
10         print(inner.__doc__)
11         return func()
12     return inner
13
14 @outer
15 def function():
16     """
17     decorator
18     asdfasd
19     :return:
20     """
21     print(‘func‘)
22
23 function()
24
25 ‘‘‘
26
27     decorator
28     asdfasd
29     :return:
30
31 func
32 ‘‘‘

含元信息

时间: 2024-12-20 00:43:17

Python 装饰器的形成过程的相关文章

5.初识python装饰器 高阶函数+闭包+函数嵌套=装饰器

一.什么是装饰器? 实际上装饰器就是个函数,这个函数可以为其他函数提供附加的功能. 装饰器在给其他函数添加功能时,不会修改原函数的源代码,不会修改原函数的调用方式. 高阶函数+函数嵌套+闭包 = 装饰器 1.1什么是高阶函数? 1.1.1函数接收的参数,包涵一个函数名. 1.1.2 函数的返回值是一个函数名. 其实这两个条件都很好满足,下面就是一个高阶函数的例子. def test1(): print "hamasaki ayumi" def test2(func): return t

python装饰器通俗易懂的解释!

python装饰器 刚刚接触python的装饰器,简直懵逼了,直接不懂什么意思啊有木有,自己都忘了走了多少遍Debug,查了多少遍资料,猜有点点开始明白了.总结了一下解释得比较好的,通俗易懂的来说明一下: 小P闲来无事,随便翻看自己以前写的一些函数,忽然对一个最最最基础的函数起了兴趣: 1 def sum1(): 2 sum = 1 + 2 3 print(sum) 4 sum1() 此时小P想看看这个函数执行用了多长时间,所以写了几句代码插进去了: 1 import time 2 3 def

python装饰器1

第八步:让装饰器带 类 参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 # -*- coding:gbk -*- '''示例8: 装饰器带类参数''' class locker:     def __init__(self):         print("locker.__init__() should be not called.")   

Python装饰器由浅入深

装饰器的功能在很多语言中都有,名字也不尽相同,其实它体现的是一种设计模式,强调的是开放封闭原则,更多的用于后期功能升级而不是编写新的代码.装饰器不光能装饰函数,也能装饰其他的对象,比如类,但通常,我们以装饰函数为例子介绍其用法.要理解在Python中装饰器的原理,需要一步一步来.本文尽量描述得浅显易懂,从最基础的内容讲起. (注:以下使用Python3.5.1环境) 一.Python的函数相关基础 第一,必须强调的是python是从上往下顺序执行的,而且碰到函数的定义代码块是不会立即执行它的,只

python 装饰器学习(decorator)

最近看到有个装饰器的例子,没看懂, #!/usr/bin/python class decorator(object): def __init__(self,f): print "initial decorator" f() def __call__(self): print "call decorator" @decorator def fun(): print "in the fun" print "after " fun

【转】九步学习python装饰器

本篇日志来自:http://www.cnblogs.com/rhcad/archive/2011/12/21/2295507.html 纯转,只字未改.只是为了学习一下装饰器.其实现在也是没有太看明白,对于装饰器我就是用的时候找例子,能蒙对,但是用过之后一段时间就忘了.还是用的少.有空应该好好看一看的,包括闭包.对于各种现代编程语言来说闭包都是很重要的.在这里先谢过原作者,如有侵权请告知. =-=-=-=-=-=-=-=-=-=-一条不怎么华丽的分隔线-=-=-=-=-=-=-=-=-=-= 这

【Python之旅】第四篇(一):Python装饰器

有时候拿到一个程序接口,需要对其进行扩展,但是又不能修改原来接口的源代码,这时候就需要使用装饰器了. 有下面一个小程序,假如是别人提供给我们的调用接口: import time def sayHi():         time.sleep(1)         print 'Hello, I am xpleaf.' 一般情况下,如果想要计算该程序的执行时间(因为有可能要对该接口进行某些性能上的测试),就需要把以上接口修改为下面这样,同时执行一下: 程序代码: import time def s

python装饰器原理及相关操作

python装饰器,简单的说就是用于操作底层代码的代码,在不改变底层代码函数的情况下对底层代码进行验证操作等 首先,必须知,道调用func和func的区别,分别为返回函数所在的内存地址和调用该函数,输出执行结果,例如: def func(): print("欢迎光临!!!") print("返回函数所在的内存地址:",func) func() 列举一个简单的web页面调用例子 1 #做登录验证 2 def login(func): 3 print("登录成

python装饰器学习笔记

什么是python装饰器? 装饰器其实也就是一个函数,一个用来包装函数的函数,返回一个修改之后的函数对象,将其重新赋值原来的标识符,并永久丧失对原始函数对象的访问. eg:当需要在Func1和Func2中加一样的功能时,可以在outer中添加一次就可以完成全部函数的添加.装饰器与函数建立连接的方式是在函数的前一行用@+装饰器名称来完成.并且在装饰器中一定要返回被装饰的对象 def outer(fun):     def wrapper():         print '验证'