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

为何使用timeit或exec函数时,函数内部变量无法在传入的全局命名空间中被查找?

解决timeit/exec中函数无法访问传入globals字典变量的问题

为什么会出现NameError?

你遇到的问题本质是Python函数的作用域绑定规则:函数内部的全局变量查找,是指向函数定义时所在的命名空间,而不是执行时传入的外部命名空间。

举个例子,你在模块级定义的fn,它的"全局作用域"默认绑定的是定义它的那个模块的globals()字典,而不是你传给timeit或exec的那个自定义字典。所以当fn内部尝试访问x时,它会去模块的全局里找,而不是你传入的{'x':20, 'fn':fn}里找——哪怕你把fn放进了这个自定义字典,也只是让执行字符串'fn()'能找到fn,但fn自己的内部查找逻辑不会变。

解决方案

下面提供几种可行的解决思路,根据你的场景选择:

1. 将x作为参数传入函数(最推荐)

这是最清晰、符合Python编码习惯的方式,直接修改函数接受x作为参数,这样就不需要依赖全局变量:

from timeit import timeit
def fn(x):
    print('This line executed.')
    return x
# 调用时传入x,自定义globals里同时包含fn和x
print(timeit('fn(x)', globals={'x': 20, 'fn': fn}, number=1_000_000))

对应exec的例子:

def fn(x):
    print('This line executed.')
    return x
exec('fn(x)', {'x': 10, 'fn': fn})

2. 在自定义命名空间中重新定义函数

如果不想修改原函数,可以把函数的定义放在timeit的setup字符串里,或者exec的执行字符串里,这样函数是在自定义命名空间中定义的,它的全局作用域就会指向这个空间:

from timeit import timeit
# setup里定义x和fn,这样fn的全局就是timeit的执行命名空间
setup = """
x = 20
def fn():
    print('This line executed.')
    return x
"""
print(timeit('fn()', setup=setup, number=1_000_000))

对应exec的例子:

s = """
x = 10
def fn():
    print('This line executed.')
    return x
fn()
"""
exec(s)

也可以用lambda简化(适合简单函数):

from timeit import timeit
print(timeit('fn()', globals={'x': 20, 'fn': lambda: x}, number=1_000_000))

3. 用functools.partial绑定变量

如果无法修改原函数的参数列表,可以用functools.partial把x绑定到函数上,生成一个不需要传参的新函数:

from timeit import timeit
from functools import partial

def fn(x):
    print('This line executed.')
    return x

# 绑定x=20到fn上
bound_fn = partial(fn, x=20)
print(timeit('bound_fn()', globals={'bound_fn': bound_fn}, number=1_000_000))

总结

不要混淆"执行代码字符串的命名空间"和"函数自身定义时的命名空间"——函数的作用域是在定义时就确定的,不会因为执行时的外部命名空间变化而改变。上面的几种方法都是通过调整函数的定义位置,或者避免依赖全局变量,来让函数能正确访问到x。

内容的提问来源于stack exchange,提问作者S.B

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 17:27:31