在线程中使用 exec(compile()) 导入问题

import issue using exec(compile()) in thread

Windows 10,Python 这里是 3.5.1 x64。

这很奇怪...假设我有这个脚本,叫做 do.py。请注意 import string 语句:

import string

# Please note that if the print statement is OUTSIDE 'main()', it works.
# It's like if 'main()' can't see the imported symbols from 'string'
def main():
    print(string.ascii_lowercase)

main()

我想在子线程中从 "launcher script" 中 运行 它,像这样 (launcher.py):

import sys
import threading

sys.argv.append('do.py')

def run(script, filename):
    exec(compile(script, filename, 'exec'))

with open(sys.argv[1], 'rb') as _:
    script = _.read()

# But this WORKS:
# exec(compile(script, sys.argv[1], 'exec'))

thread = threading.Thread(name='Runner', target=run, args=(script, sys.argv[1]))
thread.start()
thread.join()

它死于以下错误:

Exception in thread Runner:
Traceback (most recent call last):
  File "C:\Program Files\Python35\lib\threading.py", line 914, in _bootstrap_inner
    self.run()
  File "C:\Program Files\Python35\lib\threading.py", line 862, in run
    self._target(*self._args, **self._kwargs)
  File "tmpgui.py", line 7, in run
    exec(compile(script, filename, 'exec'))
  File "do.py", line 6, in <module>
    main()
  File "do.py", line 4, in main
    print(string.ascii_lowercase)
NameError: name 'string' is not defined

也就是说,执行的代码没有正确导入 string 或类似的东西,并且在 main()string 模块不可见。

这不是我的项目的完整代码,它太大了 post 这里,但我创建的模拟问题的最低限度。

以防万一有人好奇,我正在重写我的一个旧程序,它导入了脚本的 main() 函数和 运行 函数,标准输出流重定向到 tkinter 文本框。我不想从脚本中导入函数,而是想加载脚本并 运行 它。我不想使用 subprocess 出于各种原因,我更喜欢 运行 线程中的 "redirected" 代码并与处理 GUI 的主线程通信.那部分工作得很好,我唯一的问题是这个,我不明白为什么会这样!

我最好的选择:我应该将全局或本地字典中的内容传递给 exec,但我在这里迷路了...

非常感谢!

exec(thing) 等价于 exec(thing, globals(), locals()).

因此,

  • 的局部符号tabledo.pyrun函数的局部符号table
  • do.py的全局符号table是launcher.py[的全局符号table

import string导入模块并绑定到本地space中的变量,也就是run函数的本地space。您可以验证这一点:

def run(script, filename):
    try:
        exec(compile(script, filename, 'exec'))
    finally:
        assert 'string' in locals(), "won't fail because 'import' worked properly"

main 有一个单独的本地作用域,但它与 do.py 共享全局符号 table,因此与 launcher.py.

Python 试图在 main 的局部(它是空的)和全局符号 table 中找到名为 string 的变量,但失败了,并引发了NameError.

在对 exec 的调用中传递一个空字典:

def run(script, filename):
    exec(compile(script, filename, 'exec'), {})