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()函数,并且不会使用相同的钩子。 
