一道好题

一道好题

题目如下:

def f():
    l = []
    for i in range(4):
        def a(x):
            return x * i

        l.append(a)
    return l


print([e(2) for e in f()]) # [6, 6, 6, 6]

代码可以翻译成这样:

def f():
    l = []
    for i in range(4):
        def a(x):
            return x * i

        l.append(a)
    return l


l_curr = []
for e in f():
    l_curr.append(e(2))
print(l_curr)

代码的执行过程如下:

  • 代码从上到下执行,进入函数f()

  • 初始化空列表l

  • 进入for循环

  • i=0,执行l.append(a)

    python调用函数时加括号表示调用函数的最终执行结果,不加括号表示调用函数对象,所以这里列表追加的时函数对象。此时l=[<function f.<locals>.a at 内存地址1>]

  • i=1,执行l.append(a)

    此时l=[<function f.<locals>.a at 内存地址1>,<function f.<locals>.a at 内存地址2>]

  • i=2,执行l.append(a)

    此时l=[<function f.<locals>.a at 内存地址1>,<function f.<locals>.a at 内存地址2>],<function f.<locals>.a at 内存地址3>

  • i=3,执行l.append(a)

    此时l=[<function f.<locals>.a at 内存地址1>,<function f.<locals>.a at 内存地址2>],<function f.<locals>.a at 内存地址3>,<function f.<locals>.a at 内存地址4>

  • 函数f()执行完毕,return l

    此时l=[<function f.<locals>.a at 内存地址1>,<function f.<locals>.a at 内存地址2>],<function f.<locals>.a at 内存地址3>,<function f.<locals>.a at 内存地址4>

  • 初始化空列表l_curr

  • 进入for循环,开始遍历f()

    此时的f()是带括号的,所以引用的是最终返回的列表

  • 遍历第一个元素<function f.<locals>.a at 内存地址1>,执行2 * 3 = 6,这个函数存储的ii的引用,此时的i=3

  • 遍历第二个元素<function f.<locals>.a at 内存地址2>,执行2 * 3 = 6,这个函数存储的ii的引用,此时的i=3

  • 遍历第三个元素<function f.<locals>.a at 内存地址3>,执行2 * 3 = 6,这个函数存储的ii的引用,此时的i=3

  • 遍历第四个元素<function f.<locals>.a at 内存地址4>,执行2 * 3 = 6,这个函数存储的ii的引用,此时的i=3

  • 遍历完毕,返回列表l_curr=[6, 6, 6, 6]

类比

def f():
    l = []
    for i in range(4):
        def a(x, i=i):
            return x * i

        l.append(a)
    return l


l_curr = []
for e in f():
    l_curr.append(e(2))
print(l_curr)

区别是:def a(x, i=i)

在这个程序中,函数f()中的列表l每次追加的函数a中存储的i当前的i值,而不是引用。所以四次的i值分别为0, 1, 2, 3

所以最终的结果为l_curr=[0, 2, 4, 6]

原文地址:https://www.cnblogs.com/junsircoding/p/15664894.html