zoukankan      html  css  js  c++  java
  • 装饰器

    装饰器定义:

    本质是函数。函数的目的是为了完成特定的功能,那么装饰器的功能是什么呢?——饰器的功能是装饰其他函数。(为其他函数添加附加功能)。

    装饰器的原则:装饰器对被它装饰的函数是完全透明的,即意味着用着被装饰的函数根本无法感知到装饰器。

      1.不能修改被装饰的函数的源代码

      2.不能修改被装饰的函数的调用方式

    ——如,用装饰器将add()函数变为减法函数,但是add()还是认为自己是个加法函数。

    装饰器的知识储备:

      1.函数即“变量”

      2.高阶函数

      3.嵌套函数

    高阶函数+嵌套函数-->装饰器

    1.函数即“变量”

    函数即“变量”在内存中的存储引用和清空机制:

     1 #本段代码是为了说明函数即变量,另外函数是如何在内存中存储,调用和清除的。
     2 #第一段代码:
     3 def foo():
     4     print('in the foo')
     5     bar()
     6 
     7 foo()  #调用foo(),在内存中由于没有bar的函数体,也没有定义相应的函数bar,所以会报错:NameError: name 'bar' is not defined
     8 
     9 #第二段代码:
    10 def bar():
    11     print('in the bar')
    12 def foo():
    13     print('in the foo')
    14     bar()
    15 
    16 foo()  #调用foo(),返回"in the foo" "in the bar"
    17 
    18 #第三段代码:
    19 def foo():
    20     print('in the foo')
    21     bar()
    22 def bar():
    23     print('in the bar')
    24 
    25 foo()  #调用foo(),返回"in the foo" "in the bar" 
    26 
    27 
    28 #第四段代码:
    29 def foo():
    30     print('in the foo')
    31     bar()
    32 
    33 foo()  # 调用foo()
    34 
    35 def bar():
    36     print('in the bar')
    37 #返回结果报错,NameError: name 'bar' is not defined

    下图是关于函数和变量在内存中的存储,调用和清除的图示。

     综上所述,函数的函数体作为字符串的形式存放在内存开辟的一个空间内,当其赋值给一个函数名或变量时,在内存中存放着;当函数调用时,则执行在内存中的函数体;但函数没有函数名(如lambda函数),并且没有相应的变量与之相对应时,函数体在内存中会消失。

    另外调高阶函数时,无论函数的定义的顺序,只要是在调用之前,内存中都有相应的函数体和函数对应,则调用就不会出错。如果在调用函数前,内存中并没有相应的函数或变量,则一定会报错。

    2.高阶函数

    满足下列两个条件之一的就是高阶函数:

      a.把一个函数名当作实参传给另外一个函数(在不修改被装饰函数源代码的情况下为其添加功能)

      b.返回值中包含函数名(不修改函数的调用方式)

     a实例

     1 import time
     2 def bar():   #被装饰的函数
     3     time.sleep(3)
     4     print('in the bar')
     5 
     6 def test1(func):   #装饰函数,也是高阶函数,这个高阶函数实现了在不修改被装饰的函数bar的源代码的情况下为其增加了新功能
     7     start_time=time.time()  #在func()之前记录的是func()开始的时间
     8     func()
     9     stop_time=time.time()   #在func()之后,记录的是func()运行结束的时间
    10     print('the func run time is %s'%(stop_time-start_time))   #统计func()运行的总时长
    11 
    12 test1(bar)

    返回:

    b实例:

     1 import time
     2 def bar():
     3     time.sleep(3)
     4     print('in the bar')
     5 
     6 def test2(func):
     7     print(func)
     8     return func
     9 
    10 bar=test2(bar)   #将函数bar的门牌号取下另外赋值,bar代表的是test2(bar)
    11 bar()  #函数bar的调用方式未发生变化

    返回:

    3.嵌套函数

    定义:在一个函数体内创建另外一个函数,这种函数就叫内嵌函数(基于python支持静态嵌套域)

    表现的形式一定是在def函数下继续嵌套定义def函数。如下面的实例:

    1 def foo():
    2     print('in the foo')
    3     def bar():
    4         print('in the bar')
    5     bar() #嵌套函数中想要通过foo()直接调用内部嵌套的函数,必须在内部调用bar()
    6 
    7 foo()

     返回:

    以上这个案例仅仅是嵌套函数。在函数内想要通过调用最外层的函数自动触发内部定义的函数需要在内部的最外层调用内层的函数。并且调用是逐层调用的,在上面的案例中因为仅有两层,所以仅调用了内部的bar().

     写一个装饰器:

     1 import time
     2 def timer(func):    #timer(test1)  高阶函数+嵌套函数==>装饰器
     3     def deco():
     4         start_time=time.time()
     5         func()   #run test1()
     6         stop_time=time.time()
     7         print('the func run time is %s'%(stop_time-start_time))
     8     return deco  #在函数timer(func)中直接返回deco,返回deco的内存地址,供test1调用。
     9 
    10 @timer  #等价于test1=timer(test1)
    11 def test1():  #函数的源代码不变
    12     time.sleep(3)
    13     print('in the test1')
    14 
    15 test1() #函数的调用方式不变

     通过debugger断点,上面这个简单的装饰器的执行顺序为:

    1.执行import time
    
    2.def timer(func):
    
    3.@timer  #调用装饰器
    
    4.def deco():   #执行装饰器timer(),先执行def deco(),再执行return deco
    5.return deco  #返回deco的内存地址
    
    6.test1()  #调用函数test1()
    8.start_time=time.time()   #开始执行传入的deco内存地址的内容,执行deco()的代码
    9.func()   #调用test1(),执行test1()
    10.time.sleep(3)   #执行test1()
    11.print('in the test1')  #执行test1()
    12.stop_time=time.time()   #执行test1()完毕,回到deco(),执行deco后面的命令
    13.print('the func run time is %s'%(stop_time-start_time))
    结束。

    优化版(针对不同函数之间不同参数设定):

     1 import time
     2 def timer(func):    #timer(test1)  高阶函数+嵌套函数==>装饰器
     3     def deco(*args,**kwargs):   #在deco上传入不固定参数
     4         start_time=time.time()
     5         func(*args,**kwargs)  #调用不固定参数的函数
     6         stop_time=time.time()
     7         print('the func run time is %s'%(stop_time-start_time))
     8     return deco  #在函数timer(func)中直接返回deco,返回deco的内存地址,供test1调用。
     9 
    10   #等价于test1=timer(test1)
    11 @timer
    12 def test1():  #函数的源代码不变
    13     time.sleep(3)
    14     print('in the test1')
    15 
    16 @timer
    17 def test2(name):
    18     print('test2:',name)
    19 
    20 test1() #函数的调用方式不变
    21 test2('Zoe')

     只是优化了参数,执行顺序和上面的装饰器一致。

    终极版:

     1 '''需求:
     2 一个网站的页面代表一个函数,现在设定指定数量的页面需要验证登录'''
     3 
     4 import time
     5 
     6 #定义auth装饰器,用于页面登陆
     7 user,passwd='zoe','123456'
     8 def auth(auth_type):
     9     print('auth func',auth_type)
    10     def outer_wrapper(func):
    11         def wrapper(*args, **kwargs):
    12             print('wrapper func args',)
    13             if auth_type=='local':
    14                 username = input('Username:'.strip())
    15                 password = input('Password:'.strip())
    16                 if user == username and passwd == password:
    17                     print('33[32;1m User has passed authentication 33[0m')
    18                     return func(*args, **kwargs)
    19                 else:
    20                     exit('33[32;1m Invalid username or password33[0m')
    21             elif auth_type=='ldap':
    22                 print('ldap远程登陆认证')
    23 
    24 
    25 
    26         return wrapper
    27     return outer_wrapper
    28 
    29 
    30 #首页,不需要登陆
    31 def index():
    32     print('welcome to index page')
    33 
    34 #用户主页,需要登陆
    35 @auth(auth_type='local')
    36 def home():
    37     print('welcome to home page')
    38     return 'from home'
    39 
    40 #bbs论坛,需要登陆
    41 @auth(auth_type='ldap')
    42 def bbs():
    43     print('welcome to bbs page')
    44 
    45 index()
    46 home()
    47 bbs()

     用调用'@auth(auth_type='local') def home()'来debugger:

    1.import time
    2.user,passwd='zoe','123456' 
    
    3.def auth(auth_type):  
    4.@auth(auth_type='local')  #调用装饰器,输入参数auth_type='local'
    
    5.    print('auth type',auth_type)
    6.    def outer_wrapper(func):
    7.    return outer_wrapper
    
    8.        def wrapper(*args,**kwargs):
    9.        return wrapper
    
    10.home()
    
    11.            print('wrapper fun args')
    12.            if auth_type=='local':
    13.                username=input('username:'.strip())
    14.                password=input('password:'.strip()) 
    15.            print('成功登陆')
                
  • 相关阅读:
    VIVADO固化
    Keil MDK 编译器 AC5 和 AC6 优化选项重要内容和区别
    STM32时钟
    STlink/v2中SWD模式连线方式
    搭载M33内核,支持最新蓝牙5.1,晚到的DA1469x生正逢时
    超全国内外蓝牙芯片原厂总结(含芯片型号)
    芯片封装类型大全
    国务院办公厅关于2012年部分节假日安排的通知
    美国摇滚乐队Metro致敬黄家驹,全粤语翻唱Beyond经典《海阔天空》
    国务院办公厅发布2010年部分节假日安排通知
  • 原文地址:https://www.cnblogs.com/zoe233/p/7070067.html
Copyright © 2011-2022 走看看