Python - 如何让这个不可腌制的对象可腌制?
Python - How can I make this un-pickleable object pickleable?
所以,我有一个对象,里面有很多不可腌制的东西(pygame 事件、orderedDicts、时钟等),我需要将它保存到磁盘。
事情是,如果我能让这个东西存储一个有进度的字符串(我只需要一个整数),那么我可以将它传递给对象的 init,它会重建所有这些东西.不幸的是,我正在使用的框架 (Renpy) 将 腌制对象并尝试加载它,尽管我可以将它保存为单个整数,但我无法更改它.
所以,我要问的是,我如何重写方法,以便每当 pickle 尝试保存对象时,它只保存进度值,而每当它尝试加载对象时,它都会创建一个新实例从进度值?
我看过一些关于 __repr__ 方法的讨论,但我不确定在我的情况下如何使用它。
您要找的钩子是 __reduce__
。它应该 return 一个 (callable, args)
元组; callable
和 args
将被序列化,反序列化时,对象将通过 callable(*args)
重新创建。如果 class 的构造函数采用 int,则可以将 __reduce__
实现为
class ComplicatedThing:
def __reduce__(self):
return (ComplicatedThing, (self.progress_int,))
您可以将一些可选的额外内容放入元组中,当您的对象图具有循环依赖性时最有用,但您在这里不需要它们。
虽然使用 __reduce__
是执行此操作的有效方法,但正如 Python 文档所述:
Although powerful, implementing __reduce__()
directly in your classes is error prone. For this reason, class designers should use the high-level interface (i.e., __getnewargs_ex__()
, __getstate__()
and __setstate__()
) whenever possible
所以,我将解释如何使用更简单的 higher-level 接口 __getstate__
和 __setstate__
来使对象成为 picklable。
让我们以一个非常简单的 class 为例,它有一个 unpicklable 属性,假设它是一个文件句柄。
class Foo:
def __init__(self, filename):
self.filename = filename
self.f = open(filename)
Foo
的实例不可选取:
obj = Foo('test.txt')
pickle.dumps(obj)
# TypeError: cannot pickle '_io.TextIOWrapper' object
我们可以使用 pickle 分别实现 __getstate__
和 __setstate__
使这个 class 可序列化和反序列化。
class Foo:
... # the class as it was
def __getstate__(self):
"""Used for serializing instances"""
# start with a copy so we don't accidentally modify the object state
# or cause other conflicts
state = self.__dict__.copy()
# remove unpicklable entries
del state['f']
return state
def __setstate__(self, state):
"""Used for deserializing"""
# restore the state which was picklable
self.__dict__.update(state)
# restore unpicklable entries
f = open(self.filename)
self.f = f
现在可以腌制了:
obj = Foo('text.txt')
pickle.dumps(obj)
# b'\x80\x04\x951\x00\x00\x00\x00\x00\x00\x00\x8c\x08[...]'
因此,在您的示例中,您可以这样做:
class MyComplicatedObject:
def __getstate__(self):
state = self.__dict__.copy()
del state['progress'] # remove the unpicklable progress attribute
return state
def __setstate__(self, state):
self.__dict__.update(state)
# restore the progress from the progress integer
self.progress = make_progress(self.progress_int)
在Python 3.8+中你也可以实现custom reductions for objects.
所以,我有一个对象,里面有很多不可腌制的东西(pygame 事件、orderedDicts、时钟等),我需要将它保存到磁盘。
事情是,如果我能让这个东西存储一个有进度的字符串(我只需要一个整数),那么我可以将它传递给对象的 init,它会重建所有这些东西.不幸的是,我正在使用的框架 (Renpy) 将 腌制对象并尝试加载它,尽管我可以将它保存为单个整数,但我无法更改它.
所以,我要问的是,我如何重写方法,以便每当 pickle 尝试保存对象时,它只保存进度值,而每当它尝试加载对象时,它都会创建一个新实例从进度值?
我看过一些关于 __repr__ 方法的讨论,但我不确定在我的情况下如何使用它。
您要找的钩子是 __reduce__
。它应该 return 一个 (callable, args)
元组; callable
和 args
将被序列化,反序列化时,对象将通过 callable(*args)
重新创建。如果 class 的构造函数采用 int,则可以将 __reduce__
实现为
class ComplicatedThing:
def __reduce__(self):
return (ComplicatedThing, (self.progress_int,))
您可以将一些可选的额外内容放入元组中,当您的对象图具有循环依赖性时最有用,但您在这里不需要它们。
虽然使用 __reduce__
是执行此操作的有效方法,但正如 Python 文档所述:
Although powerful, implementing
__reduce__()
directly in your classes is error prone. For this reason, class designers should use the high-level interface (i.e.,__getnewargs_ex__()
,__getstate__()
and__setstate__()
) whenever possible
所以,我将解释如何使用更简单的 higher-level 接口 __getstate__
和 __setstate__
来使对象成为 picklable。
让我们以一个非常简单的 class 为例,它有一个 unpicklable 属性,假设它是一个文件句柄。
class Foo:
def __init__(self, filename):
self.filename = filename
self.f = open(filename)
Foo
的实例不可选取:
obj = Foo('test.txt')
pickle.dumps(obj)
# TypeError: cannot pickle '_io.TextIOWrapper' object
我们可以使用 pickle 分别实现 __getstate__
和 __setstate__
使这个 class 可序列化和反序列化。
class Foo:
... # the class as it was
def __getstate__(self):
"""Used for serializing instances"""
# start with a copy so we don't accidentally modify the object state
# or cause other conflicts
state = self.__dict__.copy()
# remove unpicklable entries
del state['f']
return state
def __setstate__(self, state):
"""Used for deserializing"""
# restore the state which was picklable
self.__dict__.update(state)
# restore unpicklable entries
f = open(self.filename)
self.f = f
现在可以腌制了:
obj = Foo('text.txt')
pickle.dumps(obj)
# b'\x80\x04\x951\x00\x00\x00\x00\x00\x00\x00\x8c\x08[...]'
因此,在您的示例中,您可以这样做:
class MyComplicatedObject:
def __getstate__(self):
state = self.__dict__.copy()
del state['progress'] # remove the unpicklable progress attribute
return state
def __setstate__(self, state):
self.__dict__.update(state)
# restore the progress from the progress integer
self.progress = make_progress(self.progress_int)
在Python 3.8+中你也可以实现custom reductions for objects.