为什么在使用 timeit 或 exec 函数时不会在提供的全局命名空间中搜索函数内的变量?

Why variables inside a function don't get searched in provided global namespace when using timeit or exec function?

作为一个最小的例子,我想要 timeit fn 函数,其中有一个 x 变量。所以我将 {'x': 20, 'fn': fn} 作为我的全局命名空间传递:

from timeit import timeit

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

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

我以为因为x不是本地的,所以Python检查全局命名空间来找到它。然后我返回 "This line executed.",然后是一个 NameError,上面写着:name 'x' is not defined。但是 Python 看到 {'x': 20, 'fn': fn} 命名空间,因为当我删除 'fn': fn 部分时,错误变为 name 'fn' is not defined.

exec函数也是如此(如果有帮助或有关系):

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

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

exec('fn()', {'x': 10, 'fn': fn})

如何向x介绍这些功能?将它们定义为模块级变量并将 globals() 字典传递给 global 参数是唯一的选择吗?

每个模块都有自己的全局变量, 当我们说 'fn': fn 时,我们的意思是:

  • 在它自己的模块(例如 answer.py)中使用 fn,它有自己的全局变量。

所以如果answer.py中没有像x这样的全局变量,fn就不能调用它,你会得到NameError.

这是一个例子:

# content of answer.py

x = 10


def k1():
    print(f'{globals()["x"]}')


s = """
print(globals()['x'])
"""

# uses current's module ('answer.py') global variables to run script in `s`
exec(s)

# creates new global variables to run script in `s`
exec(s, {'x': 20})

# uses current's module global variables to call `k1` in module 'answer.py' which has his own global variables
exec('k1()')

# creates new global variables to call `k1` in module 'answer.py' which has his own global variables
# when we say 'k1': k1, it means use `k1` in module 'answer.py' which has his own global variables
exec('k1()', {'k1': k1, 'x': 20})

结果:

10
20
10
10

所以如果你想告诉这些函数在你的全局变量中使用你的 x 你有一些选择,例如:

  • 更改该模块的全局变量
  • 不要使用模块函数并将其写入字符串并将全局变量传递给它

正如文档所说 globals():

Return a dictionary representing the current global symbol table. This is always the dictionary of the current module (inside a function or method, this is the module where it is "defined", not the module from which it is called).

这专门解决了我的问题。所以当 Python 试图在 fn 函数中找到 x 时,(因为它不是本地或非本地的)它会去找到它的定义并查看全局命名空间 函数定义在中,而不是我们作为全局命名空间提供的全局字典。

虽然{'x': 20, 'fn': fn}是Python可以找到标签fn的全局字典,但是fn函数本身并没有在那里定义,它只是那里。