• 装饰器


    什么是装饰器


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

    原则:

    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)

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

  • 相关阅读:
    VisualSVN-Server windows 版安装时报错 "Service 'VisualSVN Server' failed to start. Please check VisualSVN Server log in Event Viewer for more details."
    Pytest 单元测试框架之初始化和清除环境
    Pytest 单元测试框架入门
    Python(email 邮件收发)
    Python(minidom 模块)
    Python(csv 模块)
    禅道简介
    2020年最好的WooCommerce主题
    Shopify网上开店教程(2020版)
    WooCommerce VS Magento 2020:哪个跨境电商自建站软件更好?
  • 原文地址:https://www.cnblogs.com/codescrew/p/8732788.html
Copyright © 2020-2023  润新知