Python干货-函数式编程之闭包

Python干货-函数式编程之闭包

返回函数

高阶函数不仅可以返回值,还可以返回一个函数

示例:

def sum_func(*args):
    def sum():
        ax = 0
        for n in args:
            ax = ax + n
        return ax
    return sum

上例中的sum_func函数内部另定义一个函数sum,sum函数被return返回,并没有被调用执行,所以返回的结果是sum函数的引用地址,可以用变量来接收这个引用对象

# 获得sum_func函数返回的函数对象
_func = sum_func(2,3,4)
# 查看这个对象
_func
<function __main__.sum_func.<locals>.sum()>

从输出上来看,变量_func是一个方法名,而要调用方法则需要在方法名后面添加一对括号

# 调用返回的函数
_func()
9

ok,这里调用了返回的函数,并且输出了2+4+5的值。问题是,参数2, 4, 5是sum_func()的参数,_func()如何获得这些函数呢?这里需要提出闭包的概念

闭包(Closure):内部函数可以引用外部函数的参数和局部变量,当外部函数返了内部回函数时,相关参数和变量都保存在返回的函数中

注意:因为每次调用外部函数的时候,都会重新定义一个内部函数,并将其返回,这样会在计算机内存里开辟不同的内存空间,创建不同的对象,虽然内部函数名字一样,但他们的引用地址其实不一样,所以如果多次调用外部函数返回内部函数,但其实返回的对象都是不一样的

示例:

_func1 = sum_func(1, 3, 5)
_func2 = sum_func(1, 3, 5)

print(_func1 is _func2)
print('_func1 id', id(_func1))
print('_func2 id', id(_func2))
False
_func1 id 4451353872
_func2 id 4451354824

闭包

  • 必须有一个内嵌函数(函数里定义的函数)——这对应函数之间的嵌套

  • 内嵌函数必须引用一个定义在闭合范围内(外部函数里)的变量——内部函数引用外部变量

  • 外部函数必须返回内嵌函数——必须返回那个内部函数

注意:返回的函数并没有立刻执行,而是直到调用了f()才执行

# 定义一个闭包

def count():
    fs = [] 
    for i in range(1, 4):  # 循环三次,这里的i也算是闭合范围外的变量
        
        def f():  # 闭合范围
             return i*i
            
        fs.append(f)  # 将f函数保存到fs中
    return fs

f1, f2, f3 = count()  # 获得三个函数的引用

再来看调用这三个函数的结果

print(f1())
print(f2())
print(f3())
9
9
9

在得到第一个函数的时候,i的值是1;第二个函数返回的i则是2;第三个函数返回的i则是3;按常理来说,这三个函数的返回结果应该是1,4,9才对,而这里全都是9

原因:i其实是闭全范围外的变量,i是一个引用对象,在三次循环后,i的值已经变成了三;此时调用返回的三个函数,它们内部所引用的i都指向了一个内存地址,也就是说是同一个引用对象,那此时的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

f1, f2, f3 = count()
print(f1())
print(f2())
print(f3())
1
4
9

原理:再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变

匿名函数

上面的例子写得太过复杂,也不美观,如果一个函数代码块十分简洁,则可以用到匿名函数

匿名函数使用关键字lambda,语法:lambda 参数 : 表达式,示例:

_func = lambda x : x * x

上例的代码表示创建一个匿名函数(函数没有名字),将此函数的引用赋值给变量_func

冒号左边的x表示形参,如果有多个形参,则用逗号隔开

冒号右边的是执行语句,需要注意的是匿名函数的执行语句只能有一行,如果执行语句有结果,则将结果返回,如果没有结果则返回None,返回结果不需要用到return

下面来调用这个匿名函数:

_func(2)
4

匿名函数的应用:

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