Python pathlib.Path的子类对象在pickle.load之后丢失自定义属性
我正在创建一个子类Python pathlib.Path的子类对象在pickle.load之后丢失自定义属性,python,python-3.x,inheritance,serialization,pickle,Python,Python 3.x,Inheritance,Serialization,Pickle,我正在创建一个子类pathlib.Path,并想向它添加一些自定义属性。 问题是,自定义属性在被pickle重新加载后丢失。 如何解决这个问题 我尝试过的其他解决方案: 使用\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu,同样的问题 使用组合而不是继承,然后通过实现\uu getattr\uu来分派类似路径的方法。但是,在这种情况下,self.path未在pickle.load中初始化,从而导致对\uuuuuu getattr\uuuuu的无休止调用 一种方法是使用模
pathlib.Path
,并想向它添加一些自定义属性。
问题是,自定义属性在被pickle重新加载后丢失。
如何解决这个问题
我尝试过的其他解决方案:
- 使用
,同样的问题\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
- 使用组合而不是继承,然后通过实现
来分派类似路径的方法。但是,在这种情况下,\uu getattr\uu
未在self.path
中初始化,从而导致对pickle.load
的无休止调用\uuuuuu getattr\uuuuu
一种方法是使用模块将pickle支持函数关联到类的实例,如下所示。注意,我还必须修改
P
类处理参数的方式-它不再忽略这些参数
class File():
def __init__(self, *args):
self.path = Path(*args)
def __getattr__(self, item):
return getattr(self.path, item)
p = File('aaa')
p.exists() # no error
with open('xx', 'wb') as wf:
pickle.dump(p, wf)
p1 = pickle.load(open('xx', 'rb'))
# RecursionError: maximum recursion depth exceeded.
# This is due to call of self.path, in that moment, path is not in self.__dict__
输出:
import copyreg
from pathlib import Path
import pickle
class P(type(Path())):
def __init__(self, *args):
super().__init__()
self.a = args[0] if args else ''
def pickle_P(p):
print("pickling a P instance...")
return P, (p.a,)
copyreg.pickle(P, pickle_P)
p = P()
p.a = 'x'
q = P('y')
with open('xx', 'wb') as outp:
pickle.dump(p, outp)
pickle.dump(q, outp)
with open('xx', 'rb') as inp:
p1 = pickle.load(inp)
q1 = pickle.load(inp)
print('p1.a = {!r}'.format(p1.a))
print('q1.a = {!r}'.format(q1.a))
关于继承的问题。
另一个解决方案作为@martineau答案的注释
如果我是正确的,那么问题是由pathlib.PosixPath
中的\uuu reduce\uuu
方法引起的。似乎酸洗行为将由该方法确定。使用copyreg.pickle(P,pickle\u P)
的@martineau的解决方案也与此方法相关:pickle\u P
具有与\uuuu\uu
相同的返回模式
以下是关于返回值的单据:
返回元组时,元组的长度必须介于两到六项之间。
可选项可以省略,也可以不提供任何项作为其选项
价值每个项目的语义顺序如下:
- 将被调用以创建对象初始版本的可调用对象
- 可调用对象的参数元组。如果可调用对象不接受任何参数,则必须提供空元组
- (可选)对象的状态,如前所述,该状态将传递给对象的
方法。如果对象没有这样的方法,那么该值必须是一个字典,并将其添加 到对象的\uuuu setstate\uuu()
属性\uuuu dict\uuuu
\uuuu init\uuuu
这是PosixPath的源代码
def\uuuuuuuuuuuuuuuuuu(自):
#使用零件元组有助于共享内部路径零件
#当酸洗相关路径时。
#self.\u parts是传递给路径的参数
return(self.\uuuuu类,tuple(self.\u部分))
根据第三个返回值的描述,解决方案是:
从pathlib导入路径
进口泡菜
类P(类型(路径()):
定义初始化(self,*args):
super()。\uuuu init\uuuuu()
self.a=''
定义减少(自):
返回self.\u类、元组(self.\u部分)、self.\u dict__
p=p()
p、 a='x'
以open('xx','wb')作为wf:
泡菜倾倒区(p、wf)
p1=pickle.load(打开('xx','rb'))
打印(p1.a)#这里p1.a是“x”
此解决方案的缺点:
- P的实例将包含一个
属性(\uuuuu dict\uuu
使用Path
)\uuuu slots\uu
- 名为
的属性将被忽略\u hash
\uu getattr\uu()
,
可以在实例上调用\uuuu getattribute\uuuuuuuuuuuuuuuuuuuuu()
或\uuuuuuuuuu setattr\uuuuuuuuuuuuuuuuuu()
。如果这些方法依赖于某个内部不变量为真,则
类型应该实现\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
取消勾选实例时,不会调用\uuuu init\uuu()
要确保调用\uuuuuu getattr\uuuuuuu
时path
属性退出,一个解决方案是将属性分配移动到\uuuuuuu new\uuuuuu
方法中(在\uuuuu init\uuuuuuu
之前)
class文件():
定义新(cls,*参数):
obj=super()。\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuux(cls)
obj.path=path(*args)
返回obj
def _uGetAttr _;(自身,项目):
返回getattr(self.path,item)
p=文件('aaa')
p、 exists()#无错误
以open('xx','wb')作为wf:
泡菜倾倒区(p、wf)
p1=pickle.load(打开('xx','rb')#无错误
合成版怎么样?如果该错误也可以通过这种方式解决。我检查了copyreg
的文档,但是没有说明如何编写支持函数。正如我所说,这是一种方法。首先,helper函数可以以不同的方式实现,另外还有其他类似的方法,根本不涉及使用copyreg
模块。我不太清楚你所说的作曲是什么意思。如果这是一个重要的方面,请在您的问题中提及并添加(更好的)描述。谢谢,我指的是第二个代码示例中的情况。我认为组合方法的问题在于知道(或以某种方式确定)已将哪些属性添加到实例中,以便通过pickle
保存和稍后还原这些属性。我并不是说我认为这是不可能的,但我仍然不清楚你到底想完成什么,更不用说为什么必须以某种方式完成。我试图创建一个与pathlib.Path
具有相同接口的类。哇,nvm,我倒过来了
import copyreg
from pathlib import Path
import pickle
class P(type(Path())):
def __init__(self, *args):
super().__init__()
self.a = args[0] if args else ''
def pickle_P(p):
print("pickling a P instance...")
return P, (p.a,)
copyreg.pickle(P, pickle_P)
p = P()
p.a = 'x'
q = P('y')
with open('xx', 'wb') as outp:
pickle.dump(p, outp)
pickle.dump(q, outp)
with open('xx', 'rb') as inp:
p1 = pickle.load(inp)
q1 = pickle.load(inp)
print('p1.a = {!r}'.format(p1.a))
print('q1.a = {!r}'.format(q1.a))
pickling a P instance...
pickling a P instance...
p1.a = 'x'
q1.a = 'y'