• 闭包 & 装饰器函数


    了解闭包之前需要明白,全局变量与局部变量及作用域的了解。

    闭包的作用:使用闭包 该函数 经常使用时减少内存的开销

    闭包的要求:

    1、闭包必须有内嵌函数,
    2、内嵌函数必须引用该嵌套函数上一级的namespace 变量
    3、闭包函数必须返回内嵌函数
    意味着 在非全局作用域定义的 inner 函数在定义时记得外层作用域
    def func():
        b = 2
        def func2():
            return b

    装饰器作用:在不改变函数调用的方式下,在函数的前后添加功能

    装饰器的本质:闭包函数

    装饰器的基本结构:

    def wrapper(func):
        def inner(*args,**kwargs):
            """执行函数前"""
            ret = func(*args,**kwargs)
            """执行函数后"""
            return ret
        return inner    

     ************************************************************

    楔子

    作为一个会写函数的python开发,我们从今天开始要去公司上班了。写了一个函数,就交给其他开发用了。

    def func1():
        print('in func1')

    季度末,公司的领导要给大家发绩效奖金了,就提议对这段日子所有人开发的成果进行审核,审核的标准是什么呢?就是统计每个函数的执行时间。

    这个时候你要怎么做呀?你一想,这好办,把函数一改:

    def func1():
        print('in func1')
    
    你要做的就是,让你的同事依然调用func1,但是能实现调用timer方法的效果。
    import time
    def timer(func):
        start = time.time()
        func()
        print(time.time() - start)
    
    def func1():
        print('in func1')
    
    func1 =timer  #要是能这样的就完美了。。。可惜报错
    func1()
    非常可惜,上面这段代码是会报错的,因为timer方法需要传递一个func参数,我们不能在赋值的时候传参,因为只要执行func1 = timer(func1),timer方法就直接执行了,下面的那句func1根本就没有意义。到这里,我们的思路好像陷入了僵局。。。

    装饰器的形成过程

    import time
    
    def func1():
        print('in func1')
    
    def timer(func):
        def inner():
            start = time.time()
            func()
            print(time.time() - start)
        return inner
    
    func1 = timer(func1)
    func1()
    装饰器——简单版1

    忙活了这么半天,终于初具规模了!现在已经基本上完美了,唯一碍眼的那句话就是还要在做一次赋值调用。。。

    你觉得碍眼,python的开发者也觉得碍眼,所以就为我们提供了一句语法糖来解决这个问题!

    import time
    def timer(func):
        def inner():
            start = time.time()
            func()
            print(time.time() - start)
        return inner
    
    @timer   #==> func1 = timer(func1)
    def func1():
        print('in func1')
    
    
    func1()
    装饰器--语法糖

    到这里,我们可以简单的总结一下:

      装饰器的本质:一个闭包函数

      装饰器的功能:在不修改原函数及其调用方式的情况下对原函数功能进行扩展

    还有最后一个问题要解决,刚刚我们讨论的装饰器都是装饰不带参数的函数,现在要装饰一个带参数的函数怎么办呢?

    def timer(func):
        def inner(a):
            start = time.time()
            func(a)
            print(time.time() - start)
        return inner
    
    @timer
    def func1(a):
        print(a)
    
    func1(1)
    装饰器——带参数的装饰器

    其实装饰带参的函数并不是什么难事,但假如你有两个函数,需要传递的参数不一样呢?

    import time
    def timer(func):
        def inner(*args,**kwargs):
            start = time.time()
            re = func(*args,**kwargs)
            print(time.time() - start)
            return re
        return inner
    
    @timer   #==> func1 = timer(func1)
    def func1(a,b):
        print('in func1')
    
    @timer   #==> func2 = timer(func2)
    def func2(a):
        print('in func2 and get a:%s'%(a))
        return 'fun2 over'
    
    func1('aaaaaa','bbbbbb')
    print(func2('aaaaaa'))
    装饰器——成功hold住所有函数传参

     现在参数的问题已经完美的解决了,可是如果你的函数是有返回值的呢?

    import time
    def timer(func):
        def inner(*args,**kwargs):
            start = time.time()
            re = func(*args,**kwargs)
            print(time.time() - start)
            return re
        return inner
    
    @timer   #==> func2 = timer(func2)
    def func2(a):
        print('in func2 and get a:%s'%(a))
        return 'fun2 over'
    
    func2('aaaaaa','bbbbbb')
    print(func2('aaaaaa'))
    装饰器——带返回值的装饰器

    刚刚那个装饰器已经非常完美了,但是正常我们情况下查看函数的一些信息的方法在此处都会失效

    def index():
        '''这是一个主页信息'''
        print('from index')
    
    print(index.__doc__)    #查看函数注释的方法
    print(index.__name__)   #查看函数名的方法
    查看函数信息的一些方法

    为了不让他们失效,我们还要在装饰器上加上一点来完善它:

    from functools import wraps
    
    def deco(func):
        @wraps(func) #加在最内层函数正上方
        def wrapper(*args,**kwargs):
            return func(*args,**kwargs)
        return wrapper
    
    @deco
    def index():
        '''哈哈哈哈'''
        print('from index')
    
    print(index.__doc__)
    print(index.__name__)
    装饰器——wraps demo

    开放封闭原则

    1.对扩展是开放的

    为什么要对扩展开放呢?

      我们说,任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。所以我们必须允许代码扩展、添加新功能。

    2.对修改是封闭的

      就像我们刚刚提到的,因为我们写的一个函数,很有可能已经交付给其他人使用了,如果这个时候我们对其进行了修改,很有可能影响其他已经在使用该函数的用户。

    装饰器完美的遵循了这个开放封闭原则。

    装饰器的主要功能和装饰器的固定结构

    装饰器的主要功能:

    在不改变函数调用方式的基础上在函数的前、后添加功能。

    装饰器的固定格式:

    def timer(func):
        def inner(*args,**kwargs):
            '''执行函数之前要做的'''
            re = func(*args,**kwargs)
            '''执行函数之后要做的'''
            return re
        return inner
    装饰器的固定格式
    from functools import wraps
    
    def deco(func):
        @wraps(func) #加在最内层函数正上方
        def wrapper(*args,**kwargs):
            return func(*args,**kwargs)
        return wrapper
    装饰器的固定格式——wraps版

    带参数的装饰器

    假如你有成千上万个函数使用了一个装饰器,现在你想把这些装饰器都取消掉,你要怎么做?

    一个一个的取消掉? 没日没夜忙活3天。。。

    过两天你领导想通了,再让你加上。。

    def outer(flag):
        def timer(func):
            def inner(*args,**kwargs):
                if flag:
                    print('''执行函数之前要做的''')
                re = func(*args,**kwargs)
                if flag:
                    print('''执行函数之后要做的''')
                return re
            return inner
        return timer
    
    @outer(False)
    def func():
        print(111)
    
    func()
    带参数的装饰器

    执行流程分析

    F = True          #step 1 装饰器的开关变量
    def outer(flag):  #step 2
        def wrapper(func): #step 4
            def inner(*args,**kwargs): #stpe 6
                if flag:               #step 9
                    print('before')   #step 10
                    ret = func(*args,**kwargs)  #step 11  执行原函数
                    print('after')             #step13
                else:
                    ret = func(*args,**kwargs)
                    print('123')
                return ret                     #step 14
            return inner    #step 7
        return wrapper     #step 5
    
    @outer(F)   #先执行step 3 :outer(True)这个函数,然后step 6:@wrapper   #此处把开关参数传递给装饰器函数
    def hahaha():
        pass    #step 12
    hahaha()    # step 8    相当于inner()

    多个装饰器装饰同一个函数

    有些时候,我们也会用到多个装饰器装饰同一个函数的情况。

    ef wrapper1(func):
        def inner():
            print('wrapper1 ,before func')
            func()
            print('wrapper1 ,after func')
        return inner
    
    def wrapper2(func):
        def inner():
            print('wrapper2 ,before func')
            func()
            print('wrapper2 ,after func')
        return inner
    
    @wrapper2
    @wrapper1
    def f():
        print('in f')
    
    f()
    多个装饰器装饰同一个函数
  • 相关阅读:
    FJNUOJ Yehan’s hole(容斥求路径数 + 逆元)题解
    FJNUOJ the greed of Yehan(最长路 + 权值乘积转化)题解
    BZOJ 2956 模积和
    BZOJ 2299 向量
    codeforces 718c Sasha and Array
    BZOJ 3747 Kinoman
    BZOJ 2431 逆序对数列
    BZOJ 3289 Mato的文件管理
    BZOJ 3781 小B的询问
    BZOJ 2038 小Z的袜子(hose)
  • 原文地址:https://www.cnblogs.com/huyangblog/p/7789314.html
Copyright © 2020-2023  润新知