工厂方法模式与使用多处理队列冲突

时间:2014-07-09 15:29:18

标签: python design-patterns multiprocessing

我已经实现了工厂方法模式来参数化产品类的基类:

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

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

    return Wrapper

我需要通过Wrapper与使用multiprocessing模块生成的流程共享multiprocessing.Queue个对象。

由于multiprocessing.Queue使用Pickle来存储对象(请参阅Pickle documentation处的注释),并且Wrapper未在顶层定义,因此出现以下错误:

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

我使用了此answer中的解决方法,我收到了另一个错误:

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

是否有解决方案在进程间共享这些对象?

2 个答案:

答案 0 :(得分:1)

根据Pickle documentation,问题中链接的解决方法可以修改为:

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的评论。

答案 1 :(得分: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))

基本上,你在pickle时交换动态生成的Wrapper类for factory funciton + wrapped class,然后在unpickling时,再次动态生成Wrapper类(将包装类型传递给工厂) )并交换Wrapper的包装类。