<p>这是不可能的<code>exec</code>与局部变量作用域机制的交互不好,而且这种机制太受限制,无法工作。事实上,<strong>如果您使用默认的局部变量调用<code>exec</code>,则执行的字符串中的任何局部变量绑定操作都是未定义的行为</strong>,包括普通赋值、函数定义、类定义、导入等。引用<a href="https://docs.python.org/3/library/functions.html#exec" rel="nofollow noreferrer">docs</a>:</p>
<blockquote>
<p>The default locals act as described for function locals() below: modifications to the default locals dictionary should not be attempted. Pass an explicit locals dictionary if you need to see effects of the code on locals after function exec() returns. </p>
</blockquote>
<p>此外,由<code>exec</code>执行的代码不能<code>return</code>、<code>break</code>、<code>yield</code>,也不能代表调用方执行其他控制流。它可以<code>break</code>作为已执行代码的一部分的循环,或者<code>return</code>来自已执行代码中定义的函数,但是它不能与其调用方的控制流交互</p>
<hr/>
<p>如果您愿意牺牲与调用函数的局部变量交互的需求(如您在注释中所述),并且您不关心与调用方的控制流交互,那么您可以将代码的AST插入到新函数定义的主体中并执行:</p>
<pre><code>import ast
import sys
def exec_and_extract(code_string, var):
original_ast = ast.parse(code_string)
new_ast = ast.parse('def f(): return ' + var)
fdef = new_ast.body[0]
fdef.body = original_ast.body + fdef.body
code_obj = compile(new_ast, '<string>', 'exec')
gvars = sys._getframe(1).f_globals
lvars = {}
exec(code_obj, gvars, lvars)
return lvars['f']()
</code></pre>
<p>我使用了一种基于AST的方法,而不是字符串格式,以避免在输入中意外地将额外缩进插入三引号字符串等问题</p>
<p><code>inspect</code>允许我们使用调用<code>exec_and_extract</code>的人的全局变量,而不是<code>exec_and_extract</code>自己的全局变量,即使调用方位于不同的模块中</p>
<p>在执行的代码中定义的函数看到的是实际的全局变量,而不是副本</p>
<p>修改后的AST中的额外包装器函数避免了其他情况下可能出现的一些范围问题;特别是,<code>B</code>将无法在示例代码中看到<code>A</code>的定义</p>