2016-03-30 18 views
3

Cポインタを含むcythonクラスに対して__reduce__()メソッドを記述しようとしていますが、これを行う最善の方法についての情報はほとんど見つかりませんでした。 numpy配列をメンバーデータとして使用する場合、__reduce__()メソッドを正しく記述する方法については、数多くの例があります。彼らはいつもPythonオブジェクトとして格納され、Python APIとの間でコールを必要とするように、Numpy配列から離れていたいと思います。私はCのバックグラウンドから来ていますので、malloc()free()への呼び出しを使って手動でメモリを操作するのがとても快適で、pythonのやりとりを絶対最小限に抑えようとしています。Cのポインタを使ったPickle Cythonクラス

しかし、私は問題に遭遇しました。私は、最終的に使用されるPythonスクリプトから、私が作成しているクラスでcopy.deepcopy()に相当するものを使用する必要があります。私はこれを行う唯一の良い方法は、__reduce__()メソッドを実装してクラスのpickleプロトコルを実装することであることがわかりました。ほとんどのプリミティブやPythonオブジェクトではこれは簡単です。しかし、動的に割り当てられたC配列に対してこれを行う方法については、私は絶対的な犠牲を払っています。明らかに、オブジェクトが再構築されるまでに、基礎となるメモリが消えてしまうので、ポインタそのものを返すことができないので、これを行うにはどうすればよいでしょうか?これは、__reduce__()メソッドと、__init__()メソッドのいずれかまたは両方の変更が必要になると確信しています。

私は、ピクチャリング拡張タイプfound hereのほか、ちょうどthis questionのような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)) 
+0

また、私はこの質問を読んだが、それは直接の配列にCポインタを_pickling_には適用されません。 [Cython - ポインタを配列に変換してPythonオブジェクトにする](http://stackoverflow.com/questions/5271690/cython-converting-pointers-to-arrays-into-python-objects?rq=1) –

+0

私はあなたが必要と思うデータをPythonの 'bytes'オブジェクトに直列化します。次に、リビルド機能(例:http://stackoverflow.com/a/12647497/1300519)を使用してint配列にキャストし直します。私はまだこれを自分自身で動かすことはできませんでしたが、これは正しいアプローチだと私は信じています。私は実際の例があるまで答えとしてこれを掲示しない。 – Snorfalorpagus

答えて

2

一つのアプローチは、Python bytes配列にあなたの配列内のデータをシリアル化することです。 __reduce__メソッドは、最初ににデータポインタをキャストして<bytes>にキャストするget_dataメソッドを呼び出します(直接移動しようとするとCythonはそれを行う方法がわかりません)。 __reduce__set_dataメソッドを使用してインスタンスを再作成するために使用できるrebuild関数(モジュールレベルの関数でありメソッドではありません)への参照とともにこのオブジェクトを返します。あなたの例のように、複数の配列を渡す必要がある場合は、より多くの引数を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)) 
+0

データに0が含まれていると問題が発生する可能性があります(バイトが早く終了する可能性があります)。しかし、そのアイデアは私にはよく見えます。 – DavidW

+0

@DavidW私はこれについて疑問を抱いていましたが、それは問題ではないようです。 'memcpy'は他の文字列関数のようにnullバイトを考慮しません(私は思う)。私は例の配列の中央を0に設定してテストしましたが、それは問題ないと思われます。 – Snorfalorpagus

+0

memcpyはありませんが、私はバイトコンストラクタかもしれないと思った。あなたがそれをテストしたなら、それはおそらくうまくいくでしょう! – DavidW

関連する問題