使用pickle保存Cython扩展

4

我有一个用cython编写的类或扩展类型,就像这样:

cdef class Self_Organized_Map:
     cdef 
     def __cinit__(self,np.ndarray data,.....):
     ....

我使用distutils从这个名为som.pyxcython文件创建了一个Python模块,然后在python中导入并使用它来创建和训练模型,但是当我想要使用pickle保存我的模型时,它会给出以下错误:
TypeError: can't pickle som.Self_Organized_Map objects
pickle有问题吗?还是我的代码有问题?pickle不能保存扩展对象吗?

1
有很多文档和以前的问题,似乎你还没有阅读: 比如 https://docs.python.org/3/library/pickle.html#what-can-be-pickled-and-unpickled ([默认情况下扩展类型不定义__dict__] (http://cython.readthedocs.io/en/latest/src/reference/extension_types.html#attributes))。一些相关的以前的问题:https://dev59.com/UGcs5IYBdhLWcg3wrmHC https://dev59.com/LJXfa4cB1Zd3GeqPgo6o - DavidW
1
此外,您的标题提到了指针,但您的代码并没有显示任何指针。也许需要澄清一下? - DavidW
@DavidW 谢谢,我已经纠正了! - Javad Sameri
2个回答

5
Cython类默认情况下不可序列化,因此您需要自己实现Pickle interface。有许多不同的层次可以实现这一点,但__getstate____setstate__是最用户友好的级别,因此除非您有充分的理由,否则这是一个好的起点。

如果类的内容是可序列化的,那么在__getstate__中返回它们的元组,反向操作在__setstate__中进行即可。Memoryviews本身不可序列化,但可能具有可序列化的base属性。

cdef class C:
    cdef double[:] array
    cdef python_obj
    cdef int integer

    def __init__(self,array,python_obj,integer):
        self.array = array
        self.python_obj = python_obj
        self.integer = integer

    def __getstate__(self):
        return (self.array.base, # memoryviews aren't pickleable, need to get underlying object
                          # and hope it's pickleable
                self.python_obj, self.integer)

    def __setstate__(self,x):
        self.array, self.python_obj, self.integer = x

如果你的类持有 C 或 C++ 对象,那么情况就变得更加复杂了。对于简单类型,开始的好地方是将内存复制到 bytearray 中或利用 Cython 的默认 struct<->dict 互转。但是,如果该类包含指针,则这种方法行不通,你需要在 C/C++ 中实现可靠的加载/保存机制。

4
自Cython 0.26(发布于2017年7月)起,只要cdef类不包含指针或联合,它们就可以自动进行pickling。对于包含结构体的类,可以通过使用@cython.auto_pickle(True)装饰器来启用自动pickling。由于存在高代码开销和其他原因,默认情况下禁用该功能。
更多信息可以在changelogStefan Behnel的网站上找到。

比手动拾取更好的是自动序列化。 - Dee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接