• Python 基础


     装饰器的知识准备


    • 函数,函数参数
    • 作用域: 全局变量,局部变量
    • 变量解析规则:LEGB法则 - 假设嵌套函数(第二层函数),解析器查找内部函数的变量的顺序如下。 在任何一层先找到了符合要求的变量,则不再向外查找。如果没有,则抛出N
      1. Local - 本地函数内部,通过任何方式赋值的,而且没有被global关键字声明为全局变量的变量
      2. Enclosing - 直接该内部函数的外围空间(即它的上层函数)的本地作用域。多层嵌套,则有内而外逐层查找,直至最外层的函数
      3. Global - 全局空间(模块enclosed.py), 在顶层赋值的变量
      4. Buildin - 内置模块(__buildin__) 中预定义的变量名中查找变量。 
    • 变量生存周期:局部变量的生存周期随着函数的调用而存在,随着函数的结束而消亡。
    • 嵌套函数:函数中套函数并调用。
    • 高阶函数: 一个函数接受另一函数作为变量。 函数即变量
    • Python中一切皆对象(objects,之后会讲到面对对象编程的问题)。 当定义一个函数时,函数第一类对象/一级类对象。 所谓第一类对象,意思就是可以用标识符对对象命名,并且对象可以当作数据处理。例如赋值,作为参数传递给函数,或者作为返回值return等。 
    • 函数对象 vs. 函数调用 (非常容易搞混)
    def func():
        return "hello,world"
    
    ref1 = func   # 将函数对象赋值给ref1 , type(ref1)是 function 
    ref2 = func()  # 函数调用, type(ref2) 是 string
    • 闭包: 装饰器其实就是一个闭包,把一个函数当做参数然后返回一个替代版函数。所以理解闭包概念很重要。 所谓闭包,就是将组成函数的语句和这些语句的执行环境打包在一起时,得到的对象。总结:
      • 闭包最重要的使用价值:封存函数执行的上下文环境;
      • 闭包在其捕捉的执行环境(def语句快所在上下文)中,也遵循LEGB规则逐层查找,直至符合要求的变量,或者抛出异常

    装饰器


    概念:

    定义:装饰器的“器“就是函数,基本语法用def 来定义,本质就是函数,其功能是装饰其他函数,为其他函数添加附加功能

    原则:装饰器对被装饰函数完全透明

    1. 甭能修改被装饰函数的源代码
    2. 不能修改被装饰函数的调用方式

    结构:高阶函数 + 嵌套函数;

    1. 函数 即“变量“
    2. 高阶函数嵌套函数
      1. 把一个函数名当作实参传入另一个函数 ------ 在其不修改被装饰函数源代码的情况下为其添加功能
      2. 返回值中包含函数名 ----- 不修改函数的调用方式

    总结下, 装饰器就是一个返回值为函数的高阶函数,其中至少嵌套一个函数(作为返回值返回)

    工作原理:Func = Deco(Func)  用语法糖 syntax suger来表示@

    第一步: 被装饰的函数作为参数传递给装饰器函数,并执行装饰器函数, 返回值记作Newfunc

    第二步: 原函数重新赋值为Newfunc

    课堂案例 - 如何构建装饰器

    • 最简单装饰器: Moduel 2 - Vedio 7 - 装饰器的小高潮。
     1 #!user/bin/env python
     2 # -*- coding:utf-8 -*-
     3 
     4 import time
     5 
     6 
     7 def timer(func):    # timer(test1), 将test1的内存地址传给func; 其实最终通过return返回deco的内存地址
     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   # 直接返回deco函数的内存地址,如此下面test1 = deco(test1),如此test1()就可以正常调用
    14 
    15 
    16 @timer       # 就是一步运行工作,等于test1 = timer(test1) 注意此处不能加括号,因为装饰的函数,而test1()是一个返回值
    17 def test1():
    18     time.sleep(3)
    19     print('in the test1')
    20 
    21 
    22 test1()              # 此时test1 执行的是deco的内存地址,因为timer函数中的return deco
    23 print(test1)         # 被装饰过的test1的内存地址返回: <function timer.<locals>.deco at 0x00000244D3824048>
    View Code

    逻辑解释

    起始行 结束行 代码 解释
       - 4 import time  导入time模块
    4 7 def timer(func)  timer(test1), 将test1的内存地址闯入func, 最终return deco 的内存地址
    7 16  @timer

    直接跳到语法糖,执行timer装饰器

    表示: test1 = timer(test1)

    16 8 def deco()  
    8 13   return deco 返回值是deco的内存地址
    13 22 test1() 调用test1(),经过@timer的重新赋值,此时执行的是deco的内存地址,最终调用deco()
    22 9 start_time = time.time() 执行deco()    
    9 10 func() 在deco函数内部,开始调用变量func, 此时,func即test1(),   
    10 18 time.sleep(3)   执行原test1()  
    18  19 print('in the test1)  
    19 11   stop_time = time.time()  
    11 12 print('run time = %s' %(stop_time - start_time)  

    核心是内存地址的转移。

    • 被装饰的函数带参数
    #!user/bin/env python
    # -*- coding:utf-8 -*-
    
    import time
    
    
    def timer(func):    # 将test2的内存地址传给func
        def deco(*args,**kwargs):   # 用非固定参数*args, **kwargs;如此满足有参数,和没有参数的被装饰函数
            start_time = time.time()
            func(*args,**kwargs) 
            stop_time = time.time()
            print('the func run time is %s' %(stop_time-start_time))
        return deco   # 直接返回deco函数的内存地址
    
    @timer         # test2 = timer(test2)= deco; test2() = deco(); 所以当test2有函数变量,deco也需要加函数变量。
    def test2(name):  # test2本身带参数
        time.sleep(3)
        print('in the test2')
    
    test2('alex')
    View Code

    在这种情况下,

      1. 被装饰函数test2自带参数的情况下, 其对应装饰器中最终换回的内存地址的函数deco()也应带有函数。
      2. 因为装饰器最终要用于不同的被装饰函数,对于deco()的参数应用 非固定参数 *args 和 **kwargs。
    • 被装饰的函数有返回值:由于home() = wrapper(home),最终装饰过的home()返回为wrapper的内存地址的调用。 原函数的return数据也要在wrapper中写入。
     1 #!user/bin/env python
     2 # -*- coding:utf-8 -*-
     3 
     4 user,passwd = 'alex','abc123'
     5 
     6 def auth(func):
     7     def wrapper(*args,**kwargs):
     8         username = input('Username').strip()
     9         password = input('Password').strip()
    10 
    11         if user == username and passwd == password:
    12             print("33[32;1mUser has passed authorization33[0m")
    13             res = func(*args,**kwargs)    # 赋值
    14             return res                              # 返回res
    15         else:
    16             exit('33[31;1mInvalid username or password33[0m')
    17     return wrapper    # 返回wrapper的内存地址
    18 
    19 @auth(auth_type="local")
    20 def home():
    21     print('welcome to home page')
    22     return "from home"
    23 
    24 print(home())   #打印返回from home 
    view code
    • 带参数的装饰器(最终版装饰器):如果装饰器带参数,被装饰的函数有参数,那么装饰器将会有三层。
     1 #!user/bin/env python
     2 # -*- coding:utf-8 -*-
     3 
     4 # 例三:带参数的装饰器(终结版装饰器)
     5 # 情景要求: 配用多种认证方式。 home()认证方式用本地local认证,bbs()用远程的ldap
     6 
     7 user,passwd = 'alex','abc123'
     8 
     9 
    10 def auth(auth_type):   # home()的can
    11     def outer_wrapper(func):
    12         def wrapper(*args,**kwargs):
    13             username = input('Username').strip()
    14             password = input('Password').strip()
    15 
    16             if user == username and passwd == password:
    17                 print("33[32;1mUser has passed authorization33[0m")
    18                 res = func(*args,**kwargs)    # 赋值
    19                 return res                   # 返回res
    20             else:
    21                 exit('33[31;1mInvalid username or password33[0m')
    22         return wrapper    # 返回wrapper的内存地址
    23     return outer_wrapper
    24 
    25 def index():
    26     print('welcome to index page')
    27 
    28 
    29 @auth(auth_type="local")
    30 def home():
    31     print('welcome to home page')
    32     return "from home"
    33 
    34 home()
    35 
    36 @auth(auth_type="ldap")
    37 def bbs():
    38     print('welcome to bbs page')
    39 
    40 bbs()
    View Code


    Reference:

    “Python 里为什么函数可以返回一个函数内部定义的函数?”,  https://www.zhihu.com/question/25950466/answer/31731502

    “12步轻松搞定python装饰器”,http://python.jobbole.com/81683/

     

  • 相关阅读:
    eclipse pom文件报错 org.apache.maven.archiver.MavenArchiver.getManifest(org.apache.maven.project.Mav (Click for 1 more)
    严重: Compilation error org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException
    powercfg -duplicatescheme 设置电源方案
    测试3
    测试2
    markdonwn 测试1
    Java线程池-线程工厂ThreadFactory
    Java线程池-拒绝策略
    一文读懂Base64编码
    ThreadLocal
  • 原文地址:https://www.cnblogs.com/lg100lg100/p/7241601.html
Copyright © 2020-2023  润新知