工厂方法模式与多进程队列的冲突
我实现了工厂方法模式,用来给产品类的基类传递参数:
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的评论。