在处理围绕pathlib.Path对象传递的Python(3.6)程序上,我最终需要将一些额外的数据附加到路径上。我将Path子类化,并且可以正常工作。但是,在将Path子类的实例放入multiprocessing queue的put()之后,尝试从队列中获取()对象时遇到了异常(带有难以理解的错误消息...)。这是一个更简单的示例,仅使用pickle进行(反)序列化:
import pickle
from pathlib import Path
class BetterPath(type(Path())):
def __new__(cls, path_str, extra_data):
res = super().__new__(cls, path_str)
res.extra_data = extra_data
return res
subpath = BetterPath('/home/jdoe/file.txt', 'extra_data')
subpath_bytes = pickle.dumps(subpath)
subpath_obj = pickle.loads(subpath_bytes)
错误是:
Traceback (most recent call last):
File "test.py", line 12, in <module>
subpath_obj = pickle.loads(subpath_bytes)
TypeError: __new__() takes 3 positional arguments but 5 were given
更改示例代码以继承不同的不可变类型(例如int)会导致稍微不同的错误:
Traceback (most recent call last):
File "test.py", line 12, in <module>
subpath_obj = pickle.loads(subpath_bytes)
TypeError: __new__() missing 1 required positional argument: 'extra_data'
那么,如何修改我的代码以继承不可变类型(如Path)并成功反序列化实例对象?我尝试覆盖pickle module中提到的某些方法,例如__getnewargs_ex __(),但无效。
答案 0 :(得分:2)
好吧,受用户ederag的评论激励,我花了更多时间来解决这个问题。解决方案:
import pickle
from pathlib import Path
class BetterPath(type(Path())):
def __new__(cls, *parts, extra_data=None):
res = super().__new__(cls, *parts)
res.extra_data = extra_data
return res
def __reduce__(self):
return type(self), self.parts, self.__dict__, None, None
betterpath = BetterPath('/home/jdoe/file.txt', extra_data='extra_data')
betterpath_bytes = pickle.dumps(betterpath)
betterpath_obj = pickle.loads(betterpath_bytes)
最重要的是,__getnewargs__()
/ __getnewargs_ex__()
根本没有发挥作用。 (也许它们确实适用于使用__init__()
的非不可变类?)重要的方法是__reduce__()
。 pickle的文档中的描述已足够:
__setstate__()
的属性的字典新的parts
变量和拆包操作是偶然的-仅在我注意到Path / PosixPath / etc类实际上将路径部分的元组作为args之后,才添加这些变量。 (在单路径str中传递是一种特殊情况。)类似地,将extra_data
更改为关键字参数仅是由于parts
的更改-extra_data
必须是关键字,或作为第一个参数传入,并与parts
参数分开处理。