在多处理池中使用时,自定义异常不会正确引发

我正在观察Python 3.3.4中的行为,我希望能帮助理解:为什么当函数正常执行时,我的异常会正确地引发,而不是在一组工作中执行函数时呢?

import multiprocessing

class AllModuleExceptions(Exception):
    """Base class for library exceptions"""
    pass

class ModuleException_1(AllModuleExceptions):
    def __init__(self, message1):
        super(ModuleException_1, self).__init__()
        self.e_string = "Message: {}".format(message1)
        return

class ModuleException_2(AllModuleExceptions):
    def __init__(self, message2):
        super(ModuleException_2, self).__init__()
        self.e_string = "Message: {}".format(message2)
        return

def func_that_raises_exception(arg1, arg2):
    result = arg1 + arg2
    raise ModuleException_1("Something bad happened")

def func(arg1, arg2):

    try:
        result = func_that_raises_exception(arg1, arg2)

    except ModuleException_1:
        raise ModuleException_2("We need to halt main") from None

    return result

pool = multiprocessing.Pool(2)
results = pool.starmap(func, [(1,2), (3,4)])

pool.close()
pool.join()

print(results)

此代码会产生此错误:

线程线程3中的异常:
回溯(最近一次通话最后):
文件“/user/peteoss/encap/Python-3.4.2/lib/python3.4/threading.py”,第921行,在_bootstrap_inner中
self.run()
在运行文件“/user/peteoss/encap/Python-3.4.2/lib/python3.4/threading.py”,第869行
self._target(* self._args,** self._kwargs)
文件“/user/peteoss/encap/Python-3.4.2/lib/python3.4/multiprocessing/pool.py”,行420,在_handle_results中
task = get()
文件“/user/peteoss/encap/Python-3.4.2/lib/python3.4/multiprocessing/connection.py”,第251行,在recv中
返回ForkingPickler.loads(buf.getbuffer())TypeError:__init __()缺少1个必需的位置参数:'message2'

相反,如果我简单地调用函数,它似乎正确地处理异常:

print(func(1, 2))

生产:

回溯(最近一次通话最后):
文件“exceptions.py”,第40行,in
print(func(1,2))
文件“exceptions.py”,第30行,在func中
在None中引发ModuleException_2(“我们需要暂停main”)
__main __。ModuleException_2

为什么ModuleException_2在进程池中运行时的行为有所不同?


问题是你的异常类在__init__方法中有非可选参数,但是当你调用超类__init__方法时,你不会传递这些参数。 当您的异常实例被multiprocessing代码取消选中时,这会导致一个新的异常。

这是一个长期存在的Python异常问题,您可以在这个错误报告中阅读相关问题的历史记录(其中酸洗异常的一部分基础问题已修复,但不是您“重新点击)。

总结一下这个问题:Python的基本Exception类将其__init__方法接收的所有参数放入一个名为args的属性中。 这些参数被放入pickle数据中,当流被取消时,它们被传递给新创建对象的__init__方法。 如果Exception.__init__接收到的参数数量与子类的Exception.__init__不同,那么在不抽出时间的时候会出错。

解决此问题的方法是将自定义异常类在其__init__方法中要求的所有参数传递给超类__init__

class ModuleException_2(AllModuleExceptions):
    def __init__(self, message2):
        super(ModuleException_2, self).__init__(message2) # the change is here!
        self.e_string = "Message: {}".format(message2)

另一个可能的解决方法是根本不调用超类__init__方法(这是上面链接的bug中的修复所允许的),但是由于这通常是子类的不良行为,所以我不能真正推荐它。


您的ModuleException_2.__init__失败,同时ModuleException_2.__init__

我能够通过更改签名来解决问题

class ModuleException_2(AllModuleExceptions):
    def __init__(self, message2=None):
        super(ModuleException_2, self).__init__()
        self.e_string = "Message: {}".format(message2)
        return

但最好看看Pickling Class Instances以确保清洁实施。

链接地址: http://www.djcxy.com/p/56453.html

上一篇: Custom exceptions are not raised properly when used in Multiprocessing Pool

下一篇: release action to a kivy button