为何使用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
相关产品推荐
相关产品推荐

