exec() 方法的变量在哪里定义?

Where are exec() method's variables defined?

当我遇到这个问题时,我正在尝试 运行 一些将字符串转换为列表的代码。 首先这里是一个示例代码:

def str_to_list(s):
    s = s.replace('\n', '')
    exec('res="{}"'.format(str(s)))
    print(res)

s = '[0. 1. 0. 0.\n 0. 2. 5.]'
l = str_to_list(s)

当我 运行 这个时,我得到一个关于 res 的 NameError,起初我认为它可以是一种局部变量到 exec() 方法中,但后来我意识到我可以 运行 这个代码:

s = '[0. 1. 0. 0.\n 0. 2. 5.]'
s = s.replace('\n', '')
exec('res="{}"'.format(str(s)))
print(res)

因为我没有尝试从函数外部调用 res,而且我可以单独 运行 这段代码,所以我无法弄清楚问题。

我尝试在互联网上进行一些搜索,但由于我缺乏底层执行知识,我无法找到定义我的特定问题的关键字。 感谢您的帮助。

在 CPython 中,函数中的局部变量(通常?)存储在固定大小的数组中,优化称为 "fast locals"。因此,无法将局部变量动态添加到函数中。

exec 函数将尝试将新的局部变量添加到函数中,但它不会被 return 语句读取,因为固定大小的数组没有更改。

the exec docs 中所述,您需要给 exec 一个合适的 locals 字典。例如,

def str_to_list(s):
    s = s.replace('\n', '')
    d = {}
    exec('res="{}"'.format(s), None, d)
    return d['res']

s = '[0. 1. 0. 0.\n 0. 2. 5.]'
print(str_to_list(s))

输出

[0. 1. 0. 0. 0. 2. 5.]

但是,通常建议您避免使用 exec,除非您 确实 需要它。有关详细信息,请参阅 Ned Batchelder 的 Eval really is dangerous。那里的信息也适用于 exec.


如果您只想从字符串创建列表,您可以使用 ast.literal_eval。例如,

from ast import literal_eval

s = '[0, 1, 0, 0, 0, 2, 5]'
seq = literal_eval(s)
print(seq, type(seq))

输出

[0, 1, 0, 0, 0, 2, 5] <class 'list'>