• python装饰器


    Python装饰器

    在说python装饰器前,我们先来讲个故事。
    某天,公司主管过来找小明.
    主管说:“小明啊,最近公司要新增业务,需要在原来的业务上新增加一些功能,不过好多地方用到原来的地方。你好好想下,明天给我解决方啊。”
    小明不假思索地说:“好的”。
    第二天,主管来到了小明的面前。主管说:"小明啊,昨天交代你的事情怎么样了?"
    小明说:“没问题的。我打算直接修改那个业务的代码,然后再把引用到这部分的代码给修改下”
    话还没说完,主管的脸就变红了。大吼:“你知道你这样的任务量有多大吗?公司的代码量怎么多,新业务又要急于上线,你这样要改多久和多麻烦,给你一个晚上再去思考下”å
    入世未深的小明经过了一个晚上的深思熟虑。
    第三天,来到了主管面前。
    小明说:“主管,我想到了一个好办法,可以使用装饰器。不用修改原来的代码,可以在原来的基础上增加一些新的功能。”
    主管满意的点了点头。
    

    所以说,装饰器到底是什么东西呢?简而言之,装饰器就是修改其他函数功能的函数,他可以帮助我们的代码更加的简洁。重点:不需要修改原来函数里面的内容
    直接说装饰器,可能会有点难理解和接受。在说装饰器之前,我们先来学习和理解下,什么是闭包高阶函数

    高阶函数

    一个函数可以作为参数传递给另外一个函数,或者一个函数的返回值为另一个函数。满足其中的一个条件就是高阶函数。

    参数为函数的函数

    def fun1():
        print('in the fun1......')
    
    
    def fun2(fun):
        fun()
        print('in the fun2')
    
    
    fun2(fun1)
    # in the fun1
    # in the fun2
    
    

    函数的返回值为另一个函数

    def fun1(num):
        print('in the fun1')
        def fun2():
            for i in  args:
                print('in the fun2')
        return fun2()
    
    
    f = fun1(3)
    # in the fun1
    f()
    # in the fun2
    # in the fun2
    # in the fun2
    

    闭包

    我们先来看下百度百科是怎么说的

    闭包就是能够读取其他函数内部变量的函数。例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁.

    看完之后,发现一堆废话,压根看不懂。
    简单来说,一个函数在定义的时候,引用了函数外定义的变量,并且该函数可以在其定义环境被执行,这种函数我们就叫做闭包。
    先来写一个简单的闭包吧。

    def counter():
        l = [0] # 函数定义外的变量,相对于fun方法而言
        
        def fun():
            l[0] += 1 # 引用函数外定义的变量
            return l[0]
        return fun
    
    c = counter()
    print(c()) #1
    print(c()) #2
    print(c()) #3
    # c() 是fun()在其定义环境外被执行
    

    PS

    我们先来看下下面的代码

    def foo():
        l = []
        for i in range(1,4):
            def bar(i):
                return i * i
            l.append(bar)
        return l
    
    
    a, b, c = foo()
    print(a(),b(),c()) #9,9,9 
    

    结果是不是和你想的不一样,这就是典型的闭包陷阱
    原因在于l添加函数的时候,仅仅只是添加,函数没有立即执行,等到后面i的值变为3后再执行,如果不理解再仔细看看几遍吧。
    PS: 返回闭包的时候,千万不要使用循环变量或者,后续会发生改变的变量

    装饰器

    接下来,终于来到我们本章的重点环节了。

    装饰器的本质就是一个返回函数的高阶函数。

    在python中,一切都是对象。函数也是一个对象,且函数对象也是可以作为变量的,上面我们已经讨论过了。

    # 我们先来定义一个累加的函数
    def sum(num):
        s = 0
        for i in range(1, sum+1):
            s += i
        return s
    

    现在我们想要增强下sum函数的功能,计算下它累加所用的时间,这时候,就是我们装饰器上场的时候了

    import time
    
    
    def calculate_time(func):
        a = time.time()
        def wrapper(*args, **kwargs):
            a = time.time()
            res = func(*args, **kwargs)
            b = time.time()
            print("%s  执行的时间 %s " %(func.__name__, b-a))
            return res
        return wrapper
    
    
    @calculate_time
    def sum(num):
        s = 0
        for i in range(1, num+1):
            s += i
        return s
    
    print(sum(10000))
    # sum执行的时间 0.0005550384521484375 
    # 50005000
    

    是不是感觉很神奇,其实这只是把我们上面学习的做了个变形而已。
    把@calcuate_time放在函数的定义处,相当于执行语句
    sum = calculate_time(sum)
    装饰器是一个高阶函数,其返回结果是个函数,现在sum指向装饰器返回的函数,sum()执行的是返回的函数

    带参数的装饰器

    现在我们的要求又改变了,不应要打印出执行的时间,还要把累加的结果加上一个给定的整数。
     需求分析: 打印处执行时间。 这个我们在上面已经解决
    结果再加上一个给定的整数。 这个我们可以使用带参数的装饰器

    import time
    
    
    def calculate_time(num):
        def decorator(func):
            a = time.time()
            def wrapper(*args, **kwargs):
                a = time.time()
                res = func(*args, **kwargs)+num
                b = time.time()
                print("%s执行的时间 %s " %( func.__name__, b-a))
                return res
            return wrapper
        return decorator
    
    
    @calculate_time(555)
    def sum(num):
        s = 0
        for i in range(1, num+1):
            s += i
        return s
    
    print(sum(10000))
    # sum执行的时间 0.0005428791046142578 
    # 50005555
    
    print(sum.__name__)
    # wrapper
    

    如果看不懂的话,别着急,看下下面的讲解
    @calculate_time(555)加在定义函数的前面相当于sum = calculate (555)(sum)
    calculate_time(555)执行函数calculatetime(555)返回decorator,接下来来到了第二层的嵌套函数,decorator(sum)返回wrapper,这时候,sum指向wrapper
     然而现在问题来了
    sum对象变成了wrapper对象,sum的属性也都改变了,例如上面提到的sum.__name__;很容易想到,我们可以在编写wrapper.__name__=sum.__name__,但是,不需要,python的内置函数functools.wraps已经帮我们搞定,具体实现看下面代码

    不带参数的
    import time
    import funtools
    
    
    def calculate_time(func):
        a = time.time()
        @funtools.wrapper(func)
        def wrapper(*args, **kwargs):
            a = time.time()
            res = func(*args, **kwargs)
            b = time.time()
            print("%s  执行的时间 %s " %(func.__name__, b-a))
            return res
        return wrapper
    
    
    @calculate_time
    def sum(num):
        s = 0
        for i in range(1, num+1):
            s += i
        return s
    
    
    print(sum.__name__) # sum
    
    带参数的
    import time
    
    
    def calculate_time(num):
        def decorator(func):
            a = time.time()
            @functools.wrapper(func)
            def wrapper(*args, **kwargs):
                a = time.time()
                res = func(*args, **kwargs)+num
                b = time.time()
                print("%s执行的时间 %s " %( func.__name__, b-a))
                return res
            return wrapper
        return decorator
    
    
    @calculate_time(555)
    def sum(num):
        s = 0
        for i in range(1, num+1):
            s += i
        return s
    
    print(sum(10000))
    # sum执行的时间 0.0005428791046142578 
    # 50005555
    
    print(sum.__name__) # sum
    

    完结!!!!!!!!!

  • 相关阅读:
    python爬虫如何提高效率
    对 js加密数据进行爬取和解密
    爬虫之数据解析
    requests模块的基本使用
    python的零碎知识
    Django中多表关联的展示问题:
    ModelForm的基本用法:
    websocket的应用---Django
    DOM
    BOM
  • 原文地址:https://www.cnblogs.com/you-you/p/11950413.html
Copyright © 2020-2023  润新知