Python 3.6 pickling custom procedure
I have some objects of class A
which has its own method to be pickled, call it custom_module.customPickle(A)
which takes an instance of A
and returns a serialization string. I also have list of objects each of class B
that contains A
.
I need to pickle the list, but pickling A
gives some error difficult to solve. However, A
has its own method to be pickled.
I can implement the __reduce__()
method in class B
so that it calls custom_module.customPickle(A)
. But how can I do this so that pickle
is able to serialize B
efficiently?
Object A
is a music21.stream
and object B
is a custom object. The custom serialization function is music21.converter.freezeStr(streamObj, fmt=None)
and the unpickle function should be music21.converter.thawStr(strData)
You can use the copyreg
module to register custom functions for pickling and unpickling; the function you register acts like a __reduce__
method on the class.
If you return a tuple of (unpickle_function, state)
, then the registered unpickle_function
callable will be called to unpickle it again, with state as the argument, so you can use your music21.converter.thawStr()
function there:
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)
(the constructor
argument to copyreg
is ignored in recent Python versions)
This registers a global handler for those objects. You can also use a dispatch table per pickler, see [*Dispatch Tables on how you'd register one.
Now, when pickling, when encountering any instances of Stream
the handle_stream()
function is used to produce a serialisation, and the thawStr()
function will be used to unpickle that data again.
However, the music21.converter
functions use pickle themselves. They effectively pack and clean up the stream, and then pickle the resulting Stream
instance. This will then call the custom handler, and you have an infinite loop.
The work-around is to use a custom dispatch table to handle pickling and unpickling. Avoid using copyreg
in this case, as it sets a global hook that'll be called recursively each time a Stream
object is being pickled.
Your own pickle infrastructure needs to use a custom 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
Here the custom function is only called when a Stream
instance is found in your own data structure. The music21
routines use the global pickle.dumps()
and pickle.loads()
functions and won't use the same hook.
上一篇: Joblib无法正确拔除类
下一篇: Python 3.6酸洗自定义程序