You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python2与Python3中列表推导式调用函数的执行差异原因探究

Python 2 与 Python 3 执行差异原因分析

测试代码

from random import randint

functions = []

for n in range(8):

    def f():
        return f.t

    f.t = randint(1, 8)

    functions.append(f)

# 1
result = []
for f in functions:
    result.append(f())
print(result)

# 2
result = []
for f in functions:
    result.append(f.t)
print(result)

# 3
result = [f.t for f in functions]
print(result)

# 4
result = [f() for f in functions]
print(result)

现象回顾

  • 在 Python 2 中,4段代码输出完全一致。
  • 在 Python 3 中,代码段4输出8个相同值(为其他代码段输出列表的最后一个元素),与另外三段存在差异。

差异原因

核心在于Python 2和3对列表推导式的作用域处理不同,结合函数的延迟绑定特性导致了这个结果:

  • 函数的延迟绑定:循环中定义的f()函数,内部引用的f是自由变量,不会在定义时绑定到当前函数对象,而是在调用时才去外部作用域查找f对应的变量。这里的f指向全局作用域中的变量。
  • Python 2 列表推导式的作用域:Python 2的列表推导式没有独立作用域,直接使用全局作用域。代码段4的循环中,每次迭代会把当前函数对象赋值给全局变量f,调用f()时,函数内部查找的全局f就是当前迭代的对象,因此能正确返回对应的f.t。
  • Python 3 列表推导式的作用域:Python 3的列表推导式拥有独立局部作用域。代码段4中的循环变量f是推导式内部的局部变量,不会修改全局的f。而全局的f在原循环结束后,指向最后一个创建的函数对象。调用f()时,函数内部查找的是全局f,因此所有调用都返回最后一个函数的f.t值。
  • 其他代码段正常的原因:
    • 代码段1的普通for循环在两个版本中都使用外部作用域,每次迭代更新全局f,调用f()时能获取当前函数对象。
    • 代码段2和3直接访问函数对象的t属性,跳过了函数调用时的变量查找,无论作用域如何,都能直接获取当前迭代对象的属性值。

内容的提问来源于stack exchange,提问作者Penghao.Yang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 07:15:32