在通过文件路径导入模块时使用Python多进程
我正在写一个程序,使用文件路径来导入一个模块,调用的函数是 imp.load_source(module_name,module_path)
。但是,当我尝试把这个模块里的对象传递给一个 Process
时,似乎出现了问题。
举个例子:
import multiprocessing
import imp
class MyProcess(multiprocessing.Process):
def __init__(self,thing):
multiprocessing.Process.__init__(self)
self.thing=thing
def run(self):
x=self.thing
if __name__=="__main__":
module=imp.load_source('life', 'C:\\Documents and Settings\\User\\workspace\\GarlicSim\\src\\simulations\\life\\life.py')
thing=module.step
print(thing)
p=MyProcess(thing)
p.start()
注意:为了让这段代码“正常工作”,你需要把我给的 imp.load_source
的参数换成其他的:必须是你电脑上的某个Python文件,最好不要在同一个文件夹里。然后,在 thing=module.step
这行代码中,把 step 替换成那个 .py
文件里定义的某个随机函数或类。
我遇到了以下错误信息:
<function step at 0x00D5B030>
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "C:\Python26\lib\multiprocessing\forking.py", line 342, in main
self = load(from_parent)
File "C:\Python26\lib\pickle.py", line 1370, in load
return Unpickler(file).load()
File "C:\Python26\lib\pickle.py", line 858, in load
dispatch[key](self)
File "C:\Python26\lib\pickle.py", line 1090, in load_global
klass = self.find_class(module, name)
File "C:\Python26\lib\pickle.py", line 1124, in find_class
__import__(module)
ImportError: No module named life
那我该怎么办呢?
编辑:
我在Windows XP上使用的是Python 2.6.2c1。
3 个回答
我刚刚在XP系统上用Python 2.5做了以下操作...
D:\Experiments\ModuleLoading\test.py
import imp
if __name__=="__main__":
module=imp.load_source('life', r'D:\Experiments\ModuleLoading\somefolder\life.py')
thing=module.step
print(thing)
D:\Experiments\ModuleLoading\somefolder\step.py
def step():
return 'It works!'
...运行这个脚本后得到的结果是:
D:\jcage\Projects\Experiments\ModuleLoading>test.py
<function step at 0x00A0B470>
...所以先试试这个,确保模块在没有多进程的情况下可以加载吗?
[编辑] 好的,看来在分叉的进程中导入模块确实有问题。文档中有一些关于Windows的特定内容:
更多可序列化性
确保传给Process.__init__()的所有参数都是可序列化的。这意味着,特别是在Windows上,绑定或未绑定的方法不能直接作为目标参数使用——你只需定义一个函数,然后用这个函数代替。 此外,如果你继承了Process类,确保在调用Process.start()方法时,实例是可序列化的。
全局变量
要记住,如果子进程中的代码尝试访问全局变量,那么它看到的值(如果有的话)可能和在调用Process.start()时父进程中的值不一样。 不过,作为模块级常量的全局变量不会造成问题。
安全导入主模块
确保主模块可以被新的Python解释器安全导入,而不会引起意外的副作用(比如启动一个新进程)。
[编辑2] 有没有什么原因让你不能在进程中进行导入?我觉得问题在于,当你启动新进程时,它并不在同一个地址空间中,所以尝试访问原线程中的函数是行不通的。
你可以这样做,针对D:\Experiments\ModuleLoading\test.py
:
from multiprocessing import Process
import imp
class MyProcess(Process):
def __init__(self):
Process.__init__(self)
def run(self):
print 'run called...'
module=imp.load_source('life', r'D:\Experiments\ModuleLoading\somefolder\life.py')
print 'running...', module.step()
if __name__=="__main__":
p=MyProcess()
p.start()
在任何文件夹下运行的 test.py
:
import multiprocessing
import imp
class MyProcess(multiprocessing.Process):
def __init__(self,thing):
multiprocessing.Process.__init__(self)
self.thing=thing
def run(self):
print 'running...', self.thing()
if __name__=="__main__":
module=imp.load_source('life', '/tmp/life.py')
thing=module.step
print(thing)
p=MyProcess(thing)
p.start()
在 /tmp
文件夹下的 life.py
:
def step():
return 'It works!'
运行 test.py
:
$ python test.py
<function step at 0xb7dc4d4c>
running... It works!
我刚刚测试过,运行是没问题的,所以你可能是哪里搞错了。请修改你的问题,把真正出错的代码贴上来。
我使用的是 Ubuntu Jaunty 9.04,默认的 Python 版本是 (Python 2.6.2 release26-maint, 2009年4月19日)。我不知道你的问题是不是只在 Windows 上出现,因为我没有 Windows 可以测试。
这就是为什么随便改动导入路径是个坏主意。你永远无法做到在所有平台和环境下都正确,用户可能会因此感到不满。最好还是按照 Python 的方式来查找模块,也就是把模块放在 Python 的模块搜索路径上。这样在任何地方都能正常工作。
可能是因为把导入代码放在了主程序块里,所以它不工作。下面的代码在Windows XP和Python 2.6上是可以运行的。这样的话,life模块也会在新的进程中被导入。
import multiprocessing
import imp
class MyProcess(multiprocessing.Process):
def __init__(self,thing):
multiprocessing.Process.__init__(self)
self.thing=thing
def run(self):
print("Exiting self.thing")
self.thing()
print("Finished")
life=imp.load_source('life', r'd:\temp5\life.py')
if __name__=="__main__":
p=MyProcess(life.step)
p.start()