← 返回首页
Python3基础教程(二十六)
发表时间:2022-03-31 16:18:03
返回函数

1.返回函数

高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。

实例:

# -*- coding: utf-8 -*-
# @Time : 2022/3/31 15:53
# @File : returnfun.py
# @Software : PyCharm

# 定义一个函数
def test(a):
    # 在函数内部再定义一个函数,其实这个里面的函数就被认为是闭包
    def fn():
        # 这里打印一下传递进来的数字是什么
        print('I know the secret : %d' % a)

    return fn

# 调用test返回内部函数
f = test(7)
# 这个其实就是调用返回的fn函数,然后执行fn函数
f()

运行结果:

I know the secret : 7

我们注意到,返回函数里出现了类似Javascript中的闭包的概念,所谓闭包:如果在一个内部函数里对外部函数(不是在全局作用域)的变量进行引用,内部函数就被认为是闭包。

需要注意的问题是,返回的函数并没有立刻执行,而是直到调用了f()才执行。我们来看一个例子:

# -*- coding: utf-8 -*-
# @Time : 2022/3/31 16:09
# @File : closet.py
# @Software : PyCharm
def count():
    fs = []
    for i in range(1, 4):
        def f():
            return i * i

        fs.append(f)
    return fs


f1, f2, f3 = count()
print(f1())
print(f2())
print(f3())

运行结果:

9
9
9

我们发现调用f1(),f2()和f3()结果都是9,原因就在于返回的函数引用了变量i,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了3,因此最终结果为9。

如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变,改写如下:

# -*- coding: utf-8 -*-
# @Time : 2022/3/31 16:09
# @File : closet.py
# @Software : PyCharm
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

2.nonlocal

使用闭包,就是内层函数引用了外层函数的局部变量。如果只是读外层变量的值,我们会发现返回的闭包函数调用一切正常。例如:

# -*- coding: utf-8 -*-
# @Time : 2022/3/31 16:14
# @File : nolocal.py
# @Software : PyCharm

def inc():
    x = 0

    def fn():
        # 仅读取x的值:
        return x + 1

    return fn


f = inc()
print(f())  # 1
print(f())  # 1

运行结果:

1
1

但是,如果对外层变量赋值,由于Python解释器会把x当作函数fn()的局部变量,它会报错:

# -*- coding: utf-8 -*-
# @Time : 2022/3/31 16:14
# @File : nolocal.py
# @Software : PyCharm

def inc():
    x = 0
    def fn():
        #nonlocal x
        x = x + 1  #错误:原因是x作为局部变量并没有初始化,直接计算x+1是不行的。
        return x
    return fn

f = inc()
print(f()) # 1
print(f()) # 2

所以需要在fn()函数内部加一个nonlocal x的声明。加上这个声明后,解释器把fn()的x看作外层函数的局部变量,它已经被初始化了,可以正确计算x+1。

# -*- coding: utf-8 -*-
# @Time : 2022/3/31 16:14
# @File : nolocal.py
# @Software : PyCharm

def inc():
    x = 0
    def fn():
        nonlocal x
        x = x + 1  #ok
        return x
    return fn

f = inc()
print(f()) # 1
print(f()) # 2

运行结果:

1
2