Python基础(10):返回函数,闭包以及装饰器之间那些纠缠不清的关系

版权声明:转载请注明来处 https://blog.csdn.net/Daisy_zye/article/details/84783833

一:返回函数

定义:函数可以作为另一个函数的返回值。

理论来源:函数可以嵌套定义。

def fun1(li):
    def fun2():
        return sum(li)
    return fun2

调用fun1函数,返回的是一个一个fun2实例。

获取最终结果,需要执行:

test()

二:闭包

定义:在一个内部函数里,对外在作用域(不是全局作用域)的变量进行引用,这种程序结构被称为闭包。

举例:上例中的fun2是一个内部函数,它引用了li这个外部变量,li的作用域仅在fun1中,所以fun2,就是一个闭包。

闭包和返回函数的关系:闭包以返回函数的形式实现。

注意:闭包,是函数体和引用环境的整体。

警告:闭包中尽量不要引用循环变量,或后期会发生变化的变量。

实例:

def fun3():
    f=[]
    for i in range(1,4):    #循环遍历1,2,3
        def fun4():
            return i**2     #返回i的二次幂
        f.append(fun4)      #将结果追加到列表f中
    return f

测试代码如下:

for i in fun3():
    print(i())

原以为会返回一个[1,4,9]的列表,但是结果为[9,9,9]

这是因为,其实f.append(fun4)这行代码,不是将结果追加到了列表中,而是将fun4函数追加到列表中。

最终的列表f,每一个元素,都是一个闭包。

闭包是函数和运行环境的结合,在这里,函数是i**2,而环境,就是i。当三个元素被追加完时,i已经变成了3。

所以,列表f的每一个元素都是3**2,也就是9

解决这类问题的方向是:增加一层隔离环境。

通过上面的例子我们明白,结果出现偏差的主要原因是,运行环境发生变化(i发生了变化)。那么,可以这样解决:

def fun3():
    f=[]
    def fun4(j):
        def fun5():
            return j ** 2  # 返回i的二次幂
        return fun5        #返回一个闭包,函数为j**2,运行环境为j
    for i in range(1,4):    #循环遍历1,2,3
       f.append(fun4(i))     #追加元素到f,每一个元素为一个闭包,在闭包内,变量恒为i
    return f

三:装饰器

定义:在代码运行期间,动态增加功能的方式,成为装饰器。

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

与闭包的关系:是闭包的一种应用场景。

定义装饰器的两种方法:

1:利用闭包原理

双层嵌套:定义一个装饰器,接收函数作为参数,返回一个warpper闭包,在warpper内部,定义在函数执行前或后进行的操作。

实例:

import time
def log(fun):                           #定义一个装饰器,接收函数fun为参数
    def wrapper(*args,**kwargs):        #内部函数wrapper,定义具体的装饰操作
        print('excute %s'%fun.__name__) #在函数执行前,打印excute 函数名
        return fun(*args,**kwargs)      #执行函数
    return wrapper
@log                                   #用@为函数添加装饰器,相当于abc=log(abc)
def abc():                            #abc=log(abc),执行wrapper闭包,首先打印excute abc
    time.sleep(1)                      #执行函数本身,等待一秒
    print('my name is abc')           #执行打印方法
if __name__=='__main__':
    abc()

三层嵌套:适用于装饰器本身需要传入参数。

def log(text):                          #定义一个装饰器,接收普通参数text
    def decorator(fun):                #定义一个高阶函数,接收函数为参数
        def wrapper(*args,**kwargs):        #内部函数wrapper,定义具体的装饰操作
            print('%s %s'%(text,fun.__name__)) #在函数执行前,打印text 函数名
            return fun(*args,**kwargs)      #执行函数
        return wrapper                  #返回wrapper闭包
    return decorator                    #返回decorator闭包
@log('ready,go!')                      #用@为函数添加装饰器,并携带参数,相当于abc=log('ready go!')(abc)
def abc():                            #abc=log('ready go!')(abc),执行decorator闭包,再执行wrapper闭包,首先打印ready go! abc
    time.sleep(1)                      #执行函数本身,等待一秒
    print('my name is abc')           #执行打印方法

2:类装饰器

class log(object):
    def __init__(self,fun):                 #实例化需要接收一个函数
        self.fun=fun                         #将函数赋给类的fun属性,相当于接收一个函数作为参数
    def __call__(self, *args, **kwargs):    #能像方法一样被调用
        print('excute %s'%self.fun.__name__)#在函数执行前打印
        return self.fun()                   #执行函数
@log                                        #abc=log(abc),将abc作为log类的一个实例
def abc():                                 #abc已经变成一个Log对象,调用__call__方法,先打印excute abc
    time.sleep(1)                           #睡眠
    print('my name is abc')                #打印my name is abc

在有多个装饰器装饰同一个函数时,离函数近的装饰器先被装饰。

猜你喜欢

转载自blog.csdn.net/Daisy_zye/article/details/84783833