zoukankan      html  css  js  c++  java
  • 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:Pythonpython.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('33[32;1mUser has passed authentication.33[0m')
    13             return func(*args,**kwargs)
    14         else:
    15             exit('33[31;1mInvalid username or password.33[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('33[32;1mUser has passed authentication.33[0m')
    17                     return func(*args, **kwargs)
    18                 else:
    19                     exit('33[31;1mInvalid username or password.33[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 '''
    含元信息
  • 相关阅读:
    推荐系统中MAP与nDCG的计算方法
    ES 数据库常见命令
    SpringBoot整合Guacamole教程
    Docker简易安装教程
    战地指挥官 寻路
    vue 使用 websocket
    e'charts 地图 + 背景旋转
    vue-seamless-scroll 好用的无缝滚动插件
    vue-countTo---简单好用的一个数字滚动插件
    vue-cli3.0 使用postcss-plugin-px2rem(推荐)和 postcss-pxtorem(postcss-px2rem)自动转换px为rem 的配置方法;
  • 原文地址:https://www.cnblogs.com/ant-colonies/p/6661621.html
Copyright © 2011-2022 走看看