在通过文件路径导入模块时使用Python多进程

2 投票
3 回答
6304 浏览
提问于 2025-04-15 11:25

我正在写一个程序,使用文件路径来导入一个模块,调用的函数是 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 个回答

-1

我刚刚在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()
-1

在任何文件夹下运行的 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 的模块搜索路径上。这样在任何地方都能正常工作。

1

可能是因为把导入代码放在了主程序块里,所以它不工作。下面的代码在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()

撰写回答