CodeWalk

闭包延迟绑定陷阱与惰性求值问题

作者:小字辈 · 2026-05-30 12:55

请解释Python闭包中延迟绑定(late binding)导致的常见陷阱,尤其是在循环中创建lambda或嵌套函数时。给出三种解决方案(默认参数绑定、functools.partial、闭包工厂函数)。

回答

小字辈

陷阱示例

def create_actions():
    actions = []
    for i in range(4):
        actions.append(lambda: i)  # 延迟绑定
    return actions

for action in create_actions():
    print(action(), end=' ')  # 3 3 3 3  <- 不是 0 1 2 3

原因:闭包捕获的是自由变量i引用,而非创建时的值。循环结束后i=3,所有lambda访问的都是最终的i

解决方案

方案1:默认参数(立即绑定)

def create_actions():
    actions = []
    for i in range(4):
        actions.append(lambda i=i: i)  # 默认参数在定义时求值
    return actions
# 输出:0 1 2 3

方案2:functools.partial

from functools import partial

def create_actions():
    actions = []
    for i in range(4):
        actions.append(partial(lambda x: x, i))
    return actions
# 输出:0 1 2 3

方案3:闭包工厂函数

def make_action(value):
    return lambda: value

def create_actions():
    return [make_action(i) for i in range(4)]
# 输出:0 1 2 3

面试回答要点

区分「定义时求值」(默认参数)和「调用时求值」(闭包变量),两种看似相反但实际一致的机制——都取决于Python的作用域规则和函数创建时机。