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

    什么是装饰器


    从字面意义来理解“装饰器”这三个字,器指的就是函数,所以装饰器本质是一个函数,功能是为其他函数添加附加功能,举个简单的例子,一段程序你想为其增加一段统计运行时间的功能

    原则:

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

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

    如何实现一个装饰器


    装饰器=高阶函数+函数嵌套+闭包,要想实现一个装饰器,上述的知识储备不能少,现在就来讲解高阶函数+函数嵌套+闭包的基本知识,为学习装饰器做铺垫

    (1)高阶函数

      什么是高阶函数呢?其定义如下

    • 函数接受的参数是一个函数名
    • 函数返回的参数是一个函数名
    • 满足上述任意一个条件的函数就是高阶函数
    def foo():  #定义一个普通函数
        print("hello CodeScrew")
    
    def test1(Func):  #定义一个高阶函数,这个高阶函数的特征是传参为函数名
        print(Func)
    
    def test2():   #定义一个高阶函数,这个函数的特征是返回值是一个函数名
        def foo2():
            print("hello")
        return foo2
    
    test1(foo)
    test2()

    以上函数test1和test2都是高阶函数,有了这个概念我们似乎有了想法,现在我要给foo()这个函数增加打印其运行时间的做法可以实现如下

    import time
    def foo():  #定义一个普通函数
        time.sleep(0.5)  #假装这个程序需要运行0.5秒,仅仅为了演示而加
        print("hello CodeScrew")
    
    def test(Func):  #定义一个高阶函数,这个高阶函数的特征是传参为函数名
        start_time = time.time()
        Func()
        end_time = time.time()
        print(end_time - start_time)
    
    test(foo)

    利用了高阶函数的第一个性质:函数接受的参数是一个函数名,写出了以上代码。我们好像是给foo()这个函数加上了统计运行时间的功能,而且没有修改foo()函数的源代码,但是问题来了,但是这里修改了foo()这个函数的调用这显然还不满足最终的需求

    那高阶函数的第二个性质:函数的返回值是一个函数名,对我们又有什么作用呢?我们可以写出一份示例代码

    import time
    def foo():  #定义一个普通函数
        time.sleep(0.5)  #假装这个程序需要运行0.5秒,仅仅为了演示而加
        print("hello CodeScrew")
    
    def test():  #定义一个高阶函数,这里是利用第二个性质,即返回值是一个函数名
        return foo
    
    foo = test()
    foo()

    以上函数的牛逼之处在于,调用还是使用了foo(),没有修改foo()的调用方式

    当高阶函数的两个性质一起使用,我们得到了以下代码,目的还是为代码增加统计时间功能:

    import time
    def foo():  #定义一个普通函数
        time.sleep(0.5)  #假装这个程序需要运行0.5秒,仅仅为了演示而加
        print("hello CodeScrew")
    
    def test(Func):  #定义一个高阶函数,这个高阶函数的特征是传参为函数名
        start_time = time.time()
        Func()
        end_time = time.time()
        print(end_time - start_time)
        return Func
    
    foo = test(foo)
    foo()
    #打印结果为
    # hello CodeScrew
    # 0.5004322528839111
    # hello CodeScrew

    上述代码已经实现不改变源代码,不改变调用,为函数增加统计运行时间功能,但是出现了新的问题,函数被运行了两遍,如何解决这个问题呢,我们现在学习以下函数嵌套+闭包的知识,两个一起讲

    (2)函数嵌套

    (3)闭包

    什么叫函数嵌套呢,函数嵌套指的是函数定义里面还存在着函数定义,

    什么叫闭包呢,闭包实际上就是对变量的作用域的另一种说法

    用代码来讲解,下面的代码有函数嵌套,father里面定义了个son,这就是函数嵌套。

    这里面有三个包,包里面的东西就是变量(函数也是变量),比如son这个包里存了2个东西,name和granson。print这个不算是变量。

    这里有个注意点就是假如3号包中name= "王五"这一行被屏蔽,那么grandson这个print中的name就要去他的上一级包2号包中找,得到name="李四"

    好了,我们怎么利用以上的特性呢,我们已经可以实现装饰器的基本框架了,如下代码所示:

    import time
    def foo():  #定义一个普通函数
        time.sleep(0.5)  #假装这个程序需要运行0.5秒,仅仅为了演示而加
        print("hello CodeScrew")
    
    def test(Func):  #定义一个高阶函数,这个高阶函数的特征是传参为函数名
        def wrapper():   #函数嵌套
            start_time = time.time()
            Func()
            end_time = time.time()
            print(end_time -start_time)
        return wrapper   #返回嵌套的函数
    
    foo = test(foo)
    foo()

    上述代码已经可以实现不改变源代码,不改变调用的情况下为函数增加了新功能,也解决了函数被调用两次的问题,但是还存在一点小瑕疵,中间有个foo = test(foo)的操作

    对于这个瑕疵,python为我们提供了语法糖,使用@装饰器 放置在要被装饰的函数定义前面即可,如下代码所示

    import time
    
    def test(Func):  #定义一个高阶函数,这个高阶函数的特征是传参为函数名
        def wrapper():   #函数嵌套
            start_time = time.time()
            Func()
            end_time = time.time()
            print(end_time -start_time)
        return wrapper   #返回嵌套的函数
    
    @test  #这句话加上后当运行foo()的时候,相当于会自动运行一遍 foo = test(foo)的操作
    def foo():  #定义一个普通函数
        time.sleep(0.5)  #假装这个程序需要运行0.5秒,仅仅为了演示而加
        print("hello CodeScrew")
    
    foo()

     函数写到这里,觉得已经实现的很完美了,但实际上还有个漏洞,就是返回值还没有加上,试想我们运行foo(),实际上运行的是wrapper(),但是没把返回值给返回出来,加上返回值后为以下代码

    import time
    
    def test(Func):  #定义一个高阶函数,这个高阶函数的特征是传参为函数名
        def wrapper():   #函数嵌套
            start_time = time.time()
            res = Func()
            end_time = time.time()
            print(end_time -start_time)
            return res
        return wrapper   #返回嵌套的函数
    
    @test  #这句话加上后当运行foo()的时候,相当于会自动运行一遍 foo = test(foo)的操作
    def foo():  #定义一个普通函数
        time.sleep(0.5)  #假装这个程序需要运行0.5秒,仅仅为了演示而加
        print("hello CodeScrew")
        return "OK"
    
    res = foo()
    print(res)

    加入返回值之后,我们发现还有瑕疵,就是函数传参没加上。然后我们来加上函数传参

    import time
    
    def test(Func):  #定义一个高阶函数,这个高阶函数的特征是传参为函数名
        def wrapper(*args,**kwargs):   #函数嵌套
            start_time = time.time()
            res = Func(*args,**kwargs)
            end_time = time.time()
            print(end_time -start_time)
            return res
        return wrapper   #返回嵌套的函数
    
    @test  #这句话加上后当运行foo()的时候,相当于会自动运行一遍 foo = test(foo)的操作
    def foo(name,age):  #定义一个普通函数
        time.sleep(0.5)  #假装这个程序需要运行0.5秒,仅仅为了演示而加
        print("name is %s,age is %d" %(name,age))
        return "OK"
    
    res = foo("CodeScrew",25)
    print(res)

    至此就实现了一个相对比较完美的装饰器了

  • 相关阅读:
    蓄水池抽样(Reservoir Sampling )
    动态申请一个二维数组
    最大子段和问题分析和总结
    正则表达式语法
    正则表达式介绍
    小刘同学的第七十六篇博文
    小刘同学的第七十五篇博文
    小刘同学的第七十四篇博文
    小刘同学的第七十三篇博文
    小刘同学的第七十二篇博文
  • 原文地址:https://www.cnblogs.com/codescrew/p/8732788.html
Copyright © 2011-2022 走看看