廖雪峰 返回函数 2020/1/3

2020/1/3 返回函数学习笔记

1. 闭包

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(), f2(), f3())

注意:在定义 f1, f2, f3 的时候,只是调用了函数 count() 的外层,并没有调用函数 f() , 所以输出的结果是

9 9 9

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

此外,f1, f2, f3 的定义是列表常用的一种方式

a = [1, 2, 3]
b, c, d = a

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

def count():
    fs = []
    for i in range(1, 4):
        def f(i):
            def g():
                return i * i               
            return g
        fs.append(f(i))
    return fs

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

输出就变成

1 4 9

2. 练习题

利用闭包返回一个计数器函数,每次调用它返回递增整数:

1)generator 方法

应该是最先想到的,很直观的方法

def createCounter():
    def f():
        i = 0
        while True:
            i = i + 1
            yield i
    g = f()
    def counter():
        return next(g)
    return counter

注意一点,g = f() 这一步是必不可少的。

2)nonlocal 方法

def createCounter():
    i = 0
    def counter():
        nonlocal i
        i = i + 1
        return i
    return counter

通过 nonlocal 标识该变量是上一级函数中的局部变量 ,这里不推荐使用globali 变成全局变量

i = 0
def createCounter():
    def counter():
        global i
        i = i + 1
        return i
    return counter
counterA = createCounter()
counterB = createCounter()

一旦这样,counterAcounterB 会使用同一个 i, 导致结果出错

3)列表方法

想到 globalnonlocal, 自然会想到列表这类特殊情况:

def createCounter():
    s=[0]
    def counter():
        s[0]=s[0]+1
        return s[0]
    return counter  

这里注意,s 是一个列表,依旧是局部变量,但在嵌套函数中可以直接修改,列表的情况比较复杂,举一些例子:

a).
lis = [1]
def f():
#    lis.append(0)
    lis = [0]
    return lis
f()
print(lis)

这种情况列表不会改变,当然加上 global 是可以的,输出为:

[1]
b).
lis = [1]
def f():
    lis.append(0)
#    lis = [0]
    return lis
f()
print(lis)

这种情况,列表会改变,输出为:

[1, 0]

注意,上面两句话,如果同时执行,且不调换顺序的话,是会报错的,因为程序认为你是在先修改列表 lis, 再定义它

	lis.append(0)
    lis = [0]
    
local variable 'lis' referenced before assignment
c). 同样的道理,引入返回函数的情况
def f():
    lis = [1]
    def g():
#        lis.append(0)
        lis = [0]
        return lis
    g()
    print(lis)
    return g
f = f()

print(f())

通过运行可以看到,对列表整体进行操作时:

#使用 lis = [0]:
[1]
[0]

对列表具体元素进行操作时:

#使用 lis.append(0):
[1, 0]
[1, 0, 0]
d). 返回列表与返回列表元素不一样
lis = [1]
def f():
#    lis.append(0)
    lis[0] = lis[0] + 1
    return lis

print(f(),f())

返回的是列表,所以程序记住的是列表地址,最后执行两次 f() 后输出的就是:

[3] [3]

而如果,返回的是列表的元素,程序记住的就是该元素的值:

lis = [1]
def f():
#    lis.append(0)
    lis[0] = lis[0] + 1
    return lis[0]

print(f(),f())

输出为:

2 3

这里 append 操作道理是一样的。

最后,是练习题的测试:

counterA = createCounter()
print(counterA(), counterA(), counterA(), counterA(), counterA()) # 1 2 3 4 5
counterB = createCounter()
if [counterB(), counterB(), counterB(), counterB()] == [1, 2, 3, 4]:
    print('测试通过!')
else:
    print('测试失败!')

3. 参考

  1. https://blog.csdn.net/xcyansun/article/details/79672634
  2. https://www.cnblogs.com/liyang93/p/6669874.html
  3. http://pythontutor.com/visualize.html#mode=display (推荐)
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值