美文网首页
python 闭包 && 装饰器

python 闭包 && 装饰器

作者: 5a031e82bbd5 | 来源:发表于2020-04-03 00:31 被阅读0次
    • 闭包

    定义

    官方:内部函数对外部函数作用域里变量的引用

    个人理解:对于一个内部函数,如果它引用了外部函数的参数和局部变量,且外部函数返回这个内部函数,那么我们把这个内部函数成为闭包函数。闭包函数和作用域外部的变量统称为闭包。

    用法

    举例,如下是一组嵌套函数fun和fun1

    def fun():  #外部函数
        print("This is fun")
        def fun1():    #内部函数
            print("This is fun1")
    

    当我们运行fun()时,可以得到:

    This is fun

    当我们运行fun1()时,可以得到:

    Traceback (most recent call last):
    File "<input>", line 1, in <module>
    NameError: name 'fun1' is not defined

    说明内部函数不能直接被调用,func1 是建立在外部函数运行中才能调用。这里我们还需要去理解函数内部作用域的生命周期,只有执行期间才有效。如下:

    >>>fun()   #外部函数调用,内部函数创建
    >>>fun1()  #无法直接调用,创建在fun()中
    

    这样连续执行命令,我们也无法调用fun1,因为fun()中的函数只有执行期间有效。

    为此,我们要调用fun1,可以让fun外部函数返回fun1:

    def fun():
        print("This is fun")
        def fun1():
            print("This is fun1")
        return fun1
      
      test = fun()  #fun1
      
    

    这里test就为fun()返回的内部函数fun1;若要调用fun1这个函数,直接使用test()。

    此外,可以让内部函数引用外部函数作用域中的变量,使这个变量保存下来,不会失效。如下变量num存在于test()中,相当于私有化了变量,完成了数据封装。

    def fun():
        num = 1
        print("This is fun")
        def fun1():
            print(num)
        return fun1
      
    >>>test = fun()
    >>>test()
    This is fun
    1
    

    此外,py3之后,新增了nonlocal关键词,可以使内部函数修改外部函数的变量:

    def createCounter():
        n=0
        def counter():
            nonlocal n
            n = 2
            print(n)
        counter()
        print(n)
    
    def createCounter2():
        n=0
        def counter():
            n = 2
            print(n)
        counter()
        print(n)
    
    >>>createCounter()
    2
    2
    >>>createCounter2()
    2
    0
    

    注意:

    返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。

    有这样一个例子:

    def count():
        fs = []
        for i in range(1, 4):
            def f():
                 return i*i
            fs.append(f)
        return fs
    
    f1, f2, f3 = count()
    
    >>> f1()
    9
    >>> f2()
    9
    >>> f3()
    9
    

    全部都是9!原因就在于返回的函数引用了变量i,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了3,因此最终结果为9。所以我们需要另外创建一个函数;

    def count():
        def f(j):
            def g():
                return j*j
            return g
        fs = []
        for i in range(1, 4):
            fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f()
        return fs
    
    • 装饰器

    装饰器函数的标志是:@

    装饰器是基于闭包

    装饰器的作用是,不影响原函数功能,添加新的功能。他是使函数执行之前先执行装饰器的内容。如下

    def log(fun):
        def f():
            print("log装饰器之后调用Toprint函数")
            # return fun    #返回的是原函数这个对象
            return fun()  # 返回的是原函数的调用
        return f
    
    @log
    def Toprint():
        print("This is Toprint")
    
    
    
    >>>Toprint()
    log装饰器之后调用Toprint函数
    This is Toprint
    

    他的相当于log(Toprint)(),流程是log(Toprint)() --> return f --> f() --> print("log装饰器之后调用Toprint函数") --> return fun() --> print("This is Toprint")

    即接收被装饰器的函数作为参数,并进行调用一次,(如果不调用,返回的只是f函数对象),即执行f(),打印,并执行原函数;

    装饰器函数带参

    我们不能直接在装饰器函数中传入参数和函数两个参数,需要另外创建一个函数,将参数和原函数分别传入,且在内部接收函数,外部接收参数。

    def log(tcp_type):
        def func(b_fun):
            def fun():
                if tcp_type == 'get':
                    print('getget')
                if tcp_type == 'post':
                    print('postpost')
                return b_fun()
            return fun  # 返回的是原函数的调用
        return func
    
    @log(tcp_type='get')
    def get():
        print("This is get")
    
    @log(tcp_type='post')
    def post():
        print("This is post")
    

    他的相当于log(tcp_type='get')()(),流程是先调用装饰器函数,返回func函数并调用,返回fun函数并调用,判断tcp_type,打印,调用原函数。

    同时支持装饰器带参或者不带参

    即:支持:

    @log
    def f():
        pass
    

    又支持:

    @log('execute')
    def f():
        pass
    

    我们可以在装饰器函数中写一个默认变量,来进行判断;

    def log(tcp_type=None):
        def func(b_fun):
            def fun():
                if tcp_type == 'get':
                    print('getget')
                if tcp_type == 'post':
                    print('postpost')
                return b_fun()
            return fun  # 返回的是原函数的调用
        return func
    
    @log()
    def notcp():
        print("notcp")
    
    @log(tcp_type='get')
    def get():
        print("This is get")
    
    @log(tcp_type='post')
    def post():
        print("This is post")
    
    >>>notcp()
    notcp
    

    被装饰函数带参

    被装饰函数的参数可以传入装饰函数的最内部函数中进行操作

    def log(func):
        def f(x,y):
            x +=3
            y +=4
            return func(x,y)
        return f
    
    @log
    def fun(a,b):
        print(a+b)
    
    >>>fun(3,4)
    14
    

    当我们内部函数可以接受任何参数调用时,可以这样写:def fun(*args, **kw)

    def metric(fn):
        def f(*args, **kw):
            print('This is %s ' % (fn.__name__))
            return fn(*args, **kw)
        return f
    
    @metric
    def fast(x, y):
        print(x + y)
    
    @metric
    def slow(x, y, z):
        print(x * y * z)
    
    f = fast(11, 22)
    s = slow(11, 22, 33)
    

    相关文章

      网友评论

          本文标题:python 闭包 && 装饰器

          本文链接:https://www.haomeiwen.com/subject/dxwqphtx.html