带有C指针的Pickle Cython类

MS-*_*DOS 6 python pointers pickle cython

我正在尝试为__reduce__()包含C指针的cython类编写一个方法,但迄今为止找到的关于最佳方法的信息很少.关于如何__reduce__()在将numpy数组用作成员数据时正确编写方法,有大量示例.我想远离Numpy数组,因为它们似乎总是存储为python对象,并且需要调用python API.我来自一个C的背景让我很舒服的工作内存使用手动将呼叫malloc()free(),我试图保持蟒蛇相互作用降到最低.

但是我遇到了一个问题.我需要copy.deepcopy()在Python脚本中使用与我正在创建的类相同的东西,最终将使用它.我发现执行此操作的唯一好方法是通过实现__reduce__()方法为类实现pickle协议.对于大多数原语或python对象来说,这是微不足道的.但是,对于如何为动态分配的C数组执行此操作,我绝对不知所措.显然我无法返回指针本身,因为底层内存在重建对象时会消失,那么最好的方法是什么?我确信这将需要修改__reduce__()方法以及一种或两种__init__()方法.

我已经阅读了关于这里发现的 pickle扩展类型的python文档,以及关于选择cython类(例如这个问题)的堆栈溢出的所有其他问题.

我的类的精简版看起来像这样:

cdef class Bin:
    cdef int* job_ids
    cdef int* jobs
    cdef int primitive_data

    def __cinit__(self):
        self.job_ids = <int*>malloc(40 * sizeof(int))
        self.jobs = <int*>malloc(40 * sizeof(int))

    def __init__(self, int val):
        self.primitive_data = val

    def __dealloc__(self):
        free(job_ids)
        free(jobs)

    def __reduce__(self):
        return (self.__class__, (self.primitive_data))
Run Code Online (Sandbox Code Playgroud)

Sno*_*gus 5

一种方法是将数组中的数据序列化为Python bytes数组.该__reduce__方法首先调用将get_data数据指针转换为<char*>然后的方法<bytes>(如果你试图直接去那里,Cython不知道如何去做).__reduce__返回此对象,以及对rebuild函数的引用(模块级函数,而不是方法!),可以使用该set_data方法重新创建实例.如果你需要传递多个数组,就像在你的例子中一样,你只需要接受更多的参数rebuild并扩展返回的元组__reduce__.

我没有做太多测试,但它似乎工作.如果您传递格式错误的数据,它可能会爆炸.

from cpython.mem cimport PyMem_Malloc, PyMem_Realloc, PyMem_Free
from libc.string cimport memcpy

cdef int length = 40

cdef class MyClass:
    cdef long *data

    def __cinit__(self):
        self.data = <long*>PyMem_Malloc(sizeof(long)*length)
        if not self.data:
            raise MemoryError()

    cdef bytes get_data(self):
        return <bytes>(<char *>self.data)[:sizeof(long)*length]

    cdef void set_data(self, bytes data):
        memcpy(self.data, <char*>data, sizeof(long)*length)

    def set_values(self):
        # assign some dummy data to the array 0..length
        for n in range(0, length):
            self.data[n] = n

    def get(self, i):
        # get the ith value of the data
        return self.data[i]

    def __reduce__(self):
        data = self.get_data()
        return (rebuild, (data,))

    def __dealloc__(self):
        PyMem_Free(self.data)

cpdef object rebuild(bytes data):
    c = MyClass()
    c.set_data(data)
    return c
Run Code Online (Sandbox Code Playgroud)

示例用法(假设MyClass位于hello.pyx中):

import hello
import pickle

c1 = hello.MyClass()
c1.set_values()
print('c1', c1)
print('fifth item', c1.get(5))

d = pickle.dumps(c1)
del(c1)  # delete the original object

c2 = pickle.loads(d)
print('c2', c2)
print('fifth item', c2.get(5))
Run Code Online (Sandbox Code Playgroud)