zoukankan      html  css  js  c++  java
  • python之路--装饰器

    一.什么是装饰器

      首先,让我们在字面上来理解。装饰,即添加额外的修饰,在不改变函数源代码和调用方式的前提下,添加额外的功能。器,在python里面,指定的函数,例如迭代器,生成器,都是函数。装饰器,本质就是函数,功能是为其他函数添加新功能。

    二.装饰器遵循的原则(开放封闭原则)

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

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

    三.实现装饰器的原理构成

      装饰器=高级函数+函数嵌套+闭包

    四.高阶函数

      高级函数的定义:

      1.函数接受的参数是个函数名

      2.函数返回一个函数名

      3.满足以上两个函数既是高阶函数

      我们来看看用高级函数实现装饰器的效果:

    #利用高阶函数无法实现
    import time
    def foo():     #被装饰函数
        time.sleep(0.5)
        print('来自foo')
    
    def test(func):
        start_time=time.time()
        func()
        end_time=time.time()
        print("程序运行时间:%s"%(end_time-start_time))
      return func foo
    =test(foo) foo()

      我们可以发现,虽然高级函数给被修饰函数添加了额外功能,也没修改被装饰函数的调用方式,但是会多执行一次被装饰函数,与原函数实现的功能不符。所以单独使用高级函数,是无法实现装饰器功能。

    五.内嵌函数

      函数嵌套:在函数定义的内部再定义函数,称为函数嵌套。如果只是在函数中再调用其他函数,不是函数嵌套。  

    def  test():
        print('这是个错误的示范')
    
    def test1():
        test()
    非函数嵌套
    def test1():
        def test():
            print('这是内嵌函数')
    函数嵌套

    6.闭包

      闭包:在一个作用域里放入定义变量,相当于打了一个包。

    def bao1(name):
        print('这是闭包一,打包变量name,bao2 ')
        def bao2():
            print('这是闭包二,打包变量bao3')
            def bao3():
                print('这是闭包三,啥都没有打包')

      闭包补充知识:解压序列。

      可以一次性将序列赋值给多个变量。例如:

    l=[1,2,3,4,5,6]        #序列可以是字符串,列表,元组等。
    a,b,c,d,e,f=l          #序列有多少个值,则需要对应数量的变量。
    print(a,b,c,d,e,f)

      解压序列应用:获取第一个及最后一个的值

    l=[1,2,3,4,5,6]
    a,*_,b=l   #*代表中间全部,_为将中间全部抛弃 ,也可以定义为其他变量名
    print(a,b,)
    示例一
    l=[1,2,3,4,5,6]
    a,b,*c,d=l
    print('a',a,'b',b,'c',c,'d',d)
    
    输出结果:
    a 1 b 2 c [3, 4, 5] d 6
    示例二

      

    七.装饰器的实现

      利用高级函数,函数嵌套,实现装饰器。我们可以实现一个基本的装饰器。

    import time
    def test(func):
        def wrag(*args,**kwargs):  #传递被装饰函数的参数
            start_time=time.time()
            res=func(*args,**kwargs)   #返回函数的返回值
            end_time=time.time()
            print("程序运行时间:%s"%(end_time-start_time))
            return res
        return wrag
    
    def foo():
        time.sleep(0.5)
        print('来自foo')
        return 'haha'
    foo=test(foo)   
    foo()

    八.语法糖:@

      语法糖:对于计算器而言,没有任何意义,但是对于人类而言,会显得语法更友好。

      在使用装饰器时,我们需要将被装饰器函数名重新定义,如果有一万个被装饰函数,就需要重复操作该步骤一万次。这明显是不合理的。于是,出现了语法糖: @来帮我进行转换。在被装饰函数定义前,加上@装饰器函数名 即可。

    import time
    def test(func):
        def wrag():
            start_time=time.time()
            res=func()
            end_time=time.time()
            print("程序运行时间:%s"%(end_time-start_time))
            return res
        return wrag
    @test   #foo=test(foo)
    def foo():
        time.sleep(0.5)
        print('来自foo')
        return 'haha'
    
    foo()
    语法糖

    九.有参数装饰器

     如果我们要给装饰器函数传入参数,可以使用闭包,再给装饰器加一个参数。

    import time
    def test1(name):
        def test(func):
            def wrag():
                print(name)
                start_time=time.time()
                res=func()
                end_time=time.time()
                print("程序运行时间:%s"%(end_time-start_time))
                return res
            return wrag
        return test
    @test1('this is test1')   #test1(name)---->test--->foo=test(foo)
    def foo():
        time.sleep(0.5)
        print('来自foo')
        return 'haha'
    
    foo()
    View Code

      

    十.传递函数来进行装饰

    要添加的额外函数有

    def before():
        print 'before'
        
    def after():
        print 'after'

    主体函数:

    def main():
        print 'main'

    装饰器:

    复制代码
    def filter(before_func,after_func):        #要在主体函数前 和 函数后再执行的函数名
        def outer(main_func):                   
            def wrapper():
                 before_result=before_func()   #如果前函数不为None,则执行
                 if(before_result!=None):
                     return before_result
                 main_result=main_func()        #执行主体函数
                 if(main_result!=None):
                     return main_result
                 after_result=after_func()     #如果后函数不为None,则执行
                 if(after_result!=None):
                     return after_result
            return wrapper
        return outer
    复制代码

    执行结果,打印出:

    before
    main
    after

  • 相关阅读:
    关于data初始化值
    switch的优化替代写法
    phpstorm安装xdebug
    如何将一个列表封装为一个树形结构
    Win10系统桌面图标距离间距变大的问题
    cnpm无法加载文件的问题
    0、springboot
    1、springboot2新建web项目
    Game游戏分析
    netty学习
  • 原文地址:https://www.cnblogs.com/white-small/p/6889855.html
Copyright © 2011-2022 走看看