web-dev-qa-db-ja.com

Python、cPickle、ラムダ関数のピクルス

私はこのようなオブジェクトの配列をピクルスにする必要があります:

import cPickle as pickle
from numpy import sin, cos, array
tmp = lambda x: sin(x)+cos(x)
test = array([[tmp,tmp],[tmp,tmp]],dtype=object)
pickle.dump( test, open('test.lambda','w') )

そしてそれは次のエラーを出します:

TypeError: can't pickle function objects

それを回避する方法はありますか?

22

組み込みのpickleモジュールは、いくつかの種類のpythonオブジェクト(ラムダ関数、ネストされた関数、コマンドラインで定義された関数を含む)をシリアル化できません。

picloud パッケージには、ラムダ関数をピクルスできる、より堅牢なピッカーが含まれています。

from pickle import dumps
f = lambda x: x * 5
dumps(f) # error
from cloud.serialization.cloudpickle import dumps
dumps(f) # works

PiCloudでシリアル化されたオブジェクトは、通常のpickle/cPickle loadおよびloads関数を使用して逆シリアル化できます。

Dill も同様の機能を提供します

>>> import dill           
>>> f = lambda x: x * 5
>>> dill.dumps(f)
'\x80\x02cdill.dill\n_create_function\nq\x00(cdill.dill\n_unmarshal\nq\x01Uec\x01\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00C\x00\x00\x00s\x08\x00\x00\x00|\x00\x00d\x01\x00\x14S(\x02\x00\x00\x00Ni\x05\x00\x00\x00(\x00\x00\x00\x00(\x01\x00\x00\x00t\x01\x00\x00\x00x(\x00\x00\x00\x00(\x00\x00\x00\x00s\x07\x00\x00\x00<stdin>t\x08\x00\x00\x00<lambda>\x01\x00\x00\x00s\x00\x00\x00\x00q\x02\x85q\x03Rq\x04c__builtin__\n__main__\nU\x08<lambda>q\x05NN}q\x06tq\x07Rq\x08.'
23
ChrisB

代わりに、インポート可能な(別の関数内にネストされていない)実際の関数を使用する必要があります。

import cPickle as pickle
from numpy import sin, cos, array
def tmp(x):
    return sin(x)+cos(x)
test = array([[tmp,tmp],[tmp,tmp]],dtype=object)
pickle.dump( test, open('test.lambda','w') )

関数オブジェクトは引き続きlambda式で生成できますが、結果として得られる関数オブジェクトに同じ名前を付けた場合に限ります。

tmp = lambda x: sin(x)+cos(x)
tmp.__name__ = 'tmp'
test = array([[tmp, tmp], [tmp, tmp]], dtype=object)

pickleは、関数オブジェクトのモジュールと名前のみを格納するためです。上記の例では、tmp.__module__およびtmp.__name__ピクルスを外すと、同じオブジェクトが再び見つかる場所をすぐに指し示します。

9
Martijn Pieters

別の解決策があります:関数を文字列として定義し、pickle/un-pickleしてから、evalを使用します。例:

import cPickle as pickle
from numpy import sin, cos, array
tmp = "lambda x: sin(x)+cos(x)"
test = array([[tmp,tmp],[tmp,tmp]],dtype=object)
pickle.dump( test, open('test.lambda','w') )
mytmp = array([[eval(x) for x in l] for l in pickle.load(open('test.lambda','r'))])
print mytmp
# yields : [[<function <lambda> at 0x00000000033D4DD8>
#            <function <lambda> at 0x00000000033D4E48>]
#           [<function <lambda> at 0x00000000033D4EB8>
#            <function <lambda> at 0x00000000033D4F28>]]

Pickle化された表現は、外部の依存関係を使用せずに完全に自己完結するため、これは他のソリューションにとってより便利な場合があります。

5
Rabih Kodeih