Discuss / Python / 变 奏 曲

变 奏 曲

Topic source

目标

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

原始

def mycount():
    fs = []
    for i in range(1, 4):
        fs.append(lambda: i * i)
    return fs

以上造成错误答案,自然是因为调用 fs 诸项时 i 已迭代完了,旧值不再能找到。

变奏一

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

以上就只是简单地把匿名函数展开。

变奏二

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

以上是更简单的代换。但是泛函编程要注意 f 和 f() 的区别。

变奏三

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

以上和目标仅差一个字符,即形式参数故意从 i 改成 j 。但运行时以上三种变奏则和原始函数没有任何区别——错都能错成一样。

变奏三和目标的这一字之差就能说明用实际参数保存循环变量旧值的重要性。

目标的变奏

def count2():
    fs = []
    for i in range(1, 4):
        fs.append((lambda x: lambda: x * x)(i))
    return fs

以上使用匿名函数,进一步提炼出实际参数的重要性。

一个函数的局部变量一般不能被它的返回函数用赋值语句更改,除非用nonlocal关键词。

不过一个函数的局部迭代器可以被它的返回函数使用。

比如习题的计数器可以这样用生成器实现:

def createCounter():
    def C():
        c = 0
        while 1:
            c += 1
            yield c
    g = C()

    def counter():
        return next(g)
    return counter



  • 1

Reply