Python 3.6酸洗自定义程序
我有一些A
类A
对象,它有自己的方法进行pickle,将其custom_module.customPickle(A)
,它接受A
一个实例并返回一个序列化字符串。 我也有包含A
每个B
类对象的列表。
我需要腌制清单,但酸洗A
会给出一些难以解决的错误。 但是, A
有自己的方法来腌制。
我可以在类B
实现__reduce__()
方法,以便它调用custom_module.customPickle(A)
。 但我怎么能这样做,让pickle
能够有效地序列化B
?
对象A
是一个music21.stream
,对象B
是一个自定义对象。 自定义序列化函数是music21.converter.freezeStr(streamObj, fmt=None)
,unpickle函数应该是music21.converter.thawStr(strData)
您可以使用copyreg
模块注册用于酸洗和取消打印的自定义功能; 你注册的函数就像类中的__reduce__
方法一样。
如果返回一个(unpickle_function, state)
的元组,那么将调用已注册的unpickle_function
callable来重新取消它的状态作为参数,因此您可以在其中使用您的music21.converter.thawStr()
函数:
import copyreg
import music21.converter
import music21.stream
def pickle_music21_stream(stream_obj):
return music21.converter.thawStr, (music21.converter.freezeStr(stream_obj),)
copyreg.pickle(music21.stream.Stream, pickle_music21_stream)
(在最近的Python版本中, copyreg
的constructor
参数被忽略)
这为这些对象注册一个全局处理程序。 您也可以使用每个pickler的调度表,参见[* Dispatch Tables关于如何注册一个表。
现在,在酸洗时,遇到Stream
的任何实例时,将使用handle_stream()
函数来生成序列化,并且将使用thawStr()
函数来再次取消该数据。
但是, music21.converter
函数本身使用pickle。 他们有效地打包和清理流,然后pickle生成的Stream
实例。 然后这将调用自定义处理程序,并且您有一个无限循环。
解决方法是使用自定义调度表来处理酸洗和拆卸。 避免在这种情况下使用copyreg
,因为它会设置一个全局钩子,每次Stream
对象被酸洗时都会递归调用它。
您自己的泡菜基础设施需要使用自定义pickler:
import copyreg
import io
import pickle
import music21.converter
import music21.stream
def pickle_music21_stream(stream_obj):
return music21.converter.thawStr, (music21.converter.freezeStr(stream_obj),)
def dumps(obj):
f = io.BytesIO()
p = pickle.Pickler(f)
p.dispatch_table = copyreg.dispatch_table.copy()
p.dispatch_table[music21.stream.Stream] = pickle_music21_stream
p.dump(obj)
return f.getvalue()
def loads(data):
return pickle.loads(data) # hook is registered in the pickle data
这里只有在您自己的数据结构中找到Stream
实例时,才会调用自定义函数。 music21
例程使用全局pickle.dumps()
和pickle.loads()
函数,并且不会使用相同的钩子。