工厂方法模式与多进程队列的冲突

4 投票
2 回答
747 浏览
提问于 2025-04-18 12:39

我实现了工厂方法模式,用来给产品类的基类传递参数:

def factory(ParentClass):
    class Wrapper(ParentClass):
        _attr = "foo"

        def wrapped_method():
            "Do things to be done in `ParentClass`."""
            return _attr

    return Wrapper

我需要通过 multiprocessing.Queue 来和使用 multiprocessing 模块生成的进程共享 Wrapper 对象。

因为 multiprocessing.Queue 使用 Pickle 来存储对象(具体可以查看Pickle 文档),而 Wrapper 不是在顶层定义的,所以我遇到了以下错误:

PicklingError: Can't pickle <class 'Wrapper'>: attribute lookup Wrapper failed

我参考了这个回答中的解决方法,但又遇到了另一个错误:

AttributeError: ("type object 'ParentClass' has no attribute 'Wrapper'", <main._NestedClassGetter object at 0x8c7fe4c>, (<class 'ParentClass'>, 'Wrapper'))

有没有办法在进程之间共享这些类型的对象呢?

2 个回答

1

最好的解决办法是重构你的代码,避免使用动态声明的类。不过如果你不能这样做,那就需要多花点功夫来处理这些类。

你可以在你的 Wrapper 类中添加这个方法:

def __reduce__(self):
    r = super(Wrapper, self).__reduce__()
    return (wrapper_unpickler, 
            ((factory, ParentClass, r[0]) + r[1][1:])) + r[2:] 

在你的模块中添加这个函数:

def wrapper_unpickler(factory, cls, reconstructor, *args):
    return reconstructor(*((factory(cls),) + args))

简单来说,当你在保存数据时,你把动态生成的 Wrapper 类替换成一个工厂函数和被包装的类;而在读取数据时,你再动态生成 Wrapper 类(把被包装的类型传给工厂函数),然后再把被包装的类替换成 Wrapper

1

根据Pickle的文档,问题中提到的解决方法可以修改为:

class _NestedClassGetter(object):
    """
    From: http://stackoverflow.com/a/11493777/741316
    When called with the containing class as the first argument,
    and the name of the nested class as the second argument,
    returns an instance of the nested class.
    """
    def __call__(self, factory_method, base):
        nested_class = factory_method(base)

        # make an instance of a simple object (this one will do), for which we
        # can change the __class__ later on.
        nested_instance = _NestedClassGetter()

        # set the class of the instance, the __init__ will never be called on
        # the class but the original state will be set later on by pickle.
        nested_instance.__class__ = nested_class
        return nested_instance

__reduce__方法可以改成:

    def __reduce__(self):
        state = self.__dict__.copy()
        return (_NestedClassGetter(),
                (factory, ParentClass), state,)

感谢@dano的评论。

撰写回答