Pickle Cython Class 与 C 指针
Pickle Cython Class with C pointers
我正在尝试为包含 C 指针的 cython class 编写一个 __reduce__()
方法,但到目前为止,关于执行此操作的最佳方法的信息很少。关于如何在使用 numpy 数组作为成员数据时正确编写 __reduce__()
方法的示例有很多。我想远离 Numpy 数组,因为它们似乎总是存储为 python 对象,并且需要调用和调用 python API。我来自 C 语言背景,所以我很乐意使用对 malloc()
和 free()
的调用手动处理内存,并试图将 python 交互保持在绝对最低限度。
但是我 运行 遇到了问题。我需要在我正在创建的 class 上使用等同于 copy.deepcopy()
的东西,来自最终将使用它的 Python 脚本。我发现执行此操作的唯一好方法是通过实现 __reduce__()
方法来实现 class 的 pickle 协议。这对于大多数基元或 python 对象来说是微不足道的。但是,对于如何为动态分配的 C 数组执行此操作,我完全不知所措。显然我不能 return 指针本身,因为在重建对象时底层内存将消失,那么最好的方法是什么?我确信这将需要修改 __reduce__()
方法以及一个或两个 __init__()
方法。
我已阅读有关 pickling 扩展类型的 python 文档 found here as well as just about every other question of stack overflow about picking cython classes such as this question。
我的 class 的压缩版看起来像这样:
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))
一种方法是将数组中的数据序列化为 Python bytes
数组。 __reduce__
方法首先调用 get_data
方法,该方法将数据指针转换为 <char*>
然后转换为 <bytes>
(如果你试图直接去那里 Cython 不知道该怎么做它)。 __reduce__
returns 这个对象,以及对 rebuild
函数(模块级函数,而不是方法!)的引用,它可以用于使用 [=19 重新创建实例=] 方法。如果您需要传递多个数组,如您的示例所示,您只需要接受 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
示例用法(假设 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))
我正在尝试为包含 C 指针的 cython class 编写一个 __reduce__()
方法,但到目前为止,关于执行此操作的最佳方法的信息很少。关于如何在使用 numpy 数组作为成员数据时正确编写 __reduce__()
方法的示例有很多。我想远离 Numpy 数组,因为它们似乎总是存储为 python 对象,并且需要调用和调用 python API。我来自 C 语言背景,所以我很乐意使用对 malloc()
和 free()
的调用手动处理内存,并试图将 python 交互保持在绝对最低限度。
但是我 运行 遇到了问题。我需要在我正在创建的 class 上使用等同于 copy.deepcopy()
的东西,来自最终将使用它的 Python 脚本。我发现执行此操作的唯一好方法是通过实现 __reduce__()
方法来实现 class 的 pickle 协议。这对于大多数基元或 python 对象来说是微不足道的。但是,对于如何为动态分配的 C 数组执行此操作,我完全不知所措。显然我不能 return 指针本身,因为在重建对象时底层内存将消失,那么最好的方法是什么?我确信这将需要修改 __reduce__()
方法以及一个或两个 __init__()
方法。
我已阅读有关 pickling 扩展类型的 python 文档 found here as well as just about every other question of stack overflow about picking cython classes such as this question。
我的 class 的压缩版看起来像这样:
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))
一种方法是将数组中的数据序列化为 Python bytes
数组。 __reduce__
方法首先调用 get_data
方法,该方法将数据指针转换为 <char*>
然后转换为 <bytes>
(如果你试图直接去那里 Cython 不知道该怎么做它)。 __reduce__
returns 这个对象,以及对 rebuild
函数(模块级函数,而不是方法!)的引用,它可以用于使用 [=19 重新创建实例=] 方法。如果您需要传递多个数组,如您的示例所示,您只需要接受 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
示例用法(假设 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))