私は多くのスレッドでPython pickle
/cPickle
はラムダ関数をpickleできません。しかし、次のコードはPython 2.7.6:
import cPickle as pickle
if __== "__main__":
s = pickle.dumps(lambda x, y: x+y)
f = pickle.loads(s)
assert f(3,4) == 7
それで何が起こっているのでしょうか?または、むしろ、ラムダの酸洗いの制限は何ですか?
[編集]このコードが実行される理由を知っていると思います。関数を実行するタスクレットと呼ばれるマイクロスレッドの形式を持っているスタックレスpythonを実行しています。これらのタスクレットは停止、ピクル、アンピクル、および継続することができるため、関数本体をピクルする方法も提供していると推測されます(スタックレスメーリングリストで質問)。
はい、pythonはラムダ関数をピクルできますが…copy_reg
を使用して登録するものがある場合のみhowラムダ関数をピクルする-パッケージdill
は、copy_reg
のときに、必要なimport dill
をpickleレジストリにロードします。
Python 2.7.8 (default, Jul 13 2014, 02:29:54)
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> import dill # the code below will fail without this line
>>>
>>> import pickle
>>> s = pickle.dumps(lambda x, y: x+y)
>>> f = pickle.loads(s)
>>> assert f(3,4) == 7
>>> f
<function <lambda> at 0x10aebdaa0>
ここでディルを取得: https://github.com/uqfoundation
いいえ、Pythonはラムダ関数をピクルできません:
>>> import cPickle as pickle
>>> s = pickle.dumps(lambda x,y: x+y)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/copy_reg.py", line 70, in _reduce_ex
raise TypeError, "can't pickle %s objects" % base.__name__
TypeError: can't pickle function objects
成功したかわからない...
Pythonはラムダをピクルできます。 Pythonバージョンによってpickleの実装が異なるため、Python 2と3を別々に扱います。
pickle
はpickle registryを使用します。これはtype
からそのタイプのオブジェクトのシリアライズ(ピクル)に使用する関数へのマッピングにすぎません。 pickle registryは次のように表示されます。
>> pickle.Pickler.dispatch
{bool: <function pickle.save_bool>,
instance: <function pickle.save_inst>,
classobj: <function pickle.save_global>,
float: <function pickle.save_float>,
function: <function pickle.save_global>,
int: <function pickle.save_int>,
list: <function pickle.save_list>,
long: <function pickle.save_long>,
dict: <function pickle.save_dict>,
builtin_function_or_method: <function pickle.save_global>,
NoneType: <function pickle.save_none>,
str: <function pickle.save_string>,
Tuple: <function pickle.save_Tuple>,
type: <function pickle.save_global>,
unicode: <function pickle.save_unicode>}
カスタムタイプをピクルするために、Pythonは関数を登録するcopy_reg
モジュールを提供します。詳細については、こちらをご覧ください こちら 。デフォルトでは、copy_reg
moduleは次の追加タイプの酸洗いをサポートしています。
>> import copy_reg
>> copy_reg.dispatch_table
{code: <function ipykernel.codeutil.reduce_code>,
complex: <function copy_reg.pickle_complex>,
_sre.SRE_Pattern: <function re._pickle>,
posix.statvfs_result: <function os._pickle_statvfs_result>,
posix.stat_result: <function os._pickle_stat_result>}
現在、lambda
関数のタイプはtypes.FunctionType
です。ただし、このタイプの組み込み関数function: <function pickle.save_global>
は、ラムダ関数をシリアル化できません。したがって、dill
、cloudpickle
などのすべてのサードパーティライブラリは、組み込みメソッドをオーバーライドして、いくつかの追加ロジックでラムダ関数をシリアル化します。 dill
をインポートして、その機能を見てみましょう。
>> import dill
>> pickle.Pickler.dispatch
{_pyio.BufferedReader: <function dill.dill.save_file>,
_pyio.TextIOWrapper: <function dill.dill.save_file>,
_pyio.BufferedWriter: <function dill.dill.save_file>,
_pyio.BufferedRandom: <function dill.dill.save_file>,
functools.partial: <function dill.dill.save_functor>,
operator.attrgetter: <function dill.dill.save_attrgetter>,
operator.itemgetter: <function dill.dill.save_itemgetter>,
cStringIO.StringI: <function dill.dill.save_stringi>,
cStringIO.StringO: <function dill.dill.save_stringo>,
bool: <function pickle.save_bool>,
cell: <function dill.dill.save_cell>,
instancemethod: <function dill.dill.save_instancemethod0>,
instance: <function pickle.save_inst>,
classobj: <function dill.dill.save_classobj>,
code: <function dill.dill.save_code>,
property: <function dill.dill.save_property>,
method-wrapper: <function dill.dill.save_instancemethod>,
dictproxy: <function dill.dill.save_dictproxy>,
wrapper_descriptor: <function dill.dill.save_wrapper_descriptor>,
getset_descriptor: <function dill.dill.save_wrapper_descriptor>,
member_descriptor: <function dill.dill.save_wrapper_descriptor>,
method_descriptor: <function dill.dill.save_wrapper_descriptor>,
file: <function dill.dill.save_file>,
float: <function pickle.save_float>,
staticmethod: <function dill.dill.save_classmethod>,
classmethod: <function dill.dill.save_classmethod>,
function: <function dill.dill.save_function>,
int: <function pickle.save_int>,
list: <function pickle.save_list>,
long: <function pickle.save_long>,
dict: <function dill.dill.save_module_dict>,
builtin_function_or_method: <function dill.dill.save_builtin_method>,
module: <function dill.dill.save_module>,
NotImplementedType: <function dill.dill.save_singleton>,
NoneType: <function pickle.save_none>,
xrange: <function dill.dill.save_singleton>,
slice: <function dill.dill.save_slice>,
Ellipsis: <function dill.dill.save_singleton>,
str: <function pickle.save_string>,
Tuple: <function pickle.save_Tuple>,
super: <function dill.dill.save_functor>,
type: <function dill.dill.save_type>,
weakcallableproxy: <function dill.dill.save_weakproxy>,
weakproxy: <function dill.dill.save_weakproxy>,
weakref: <function dill.dill.save_weakref>,
unicode: <function pickle.save_unicode>,
thread.lock: <function dill.dill.save_lock>}
それでは、ラムダ関数をピクルスしてみましょう。
>> pickle.loads(pickle.dumps(lambda x:x))
<function __main__.<lambda>>
できます!!
Python 2には、pickle
の2つのバージョンがあります-
import pickle # pure Python version
pickle.__file__ # <install directory>/python-2.7/lib64/python2.7/pickle.py
import cPickle # C extension
cPickle.__file__ # <install directory>/python-2.7/lib64/python2.7/lib-dynload/cPickle.so
それでは、C実装cPickle
でラムダをピクルスしてみましょう。
>> import cPickle
>> cPickle.loads(cPickle.dumps(lambda x:x))
TypeError: can't pickle function objects
何が悪かったのか? cPickle
のディスパッチテーブルを見てみましょう。
>> cPickle.Pickler.dispatch_table
AttributeError: 'builtin_function_or_method' object has no attribute 'dispatch_table'
pickle
とcPickle
の実装は異なります。 Importing
dillはPythonバージョンのpickle
のみを機能させます。 pickle
の代わりにcPickle
を使用することの欠点は、cPickleよりも1000回遅くなる可能性があることです。
Python 3には、cPickle
という名前のモジュールはありません。代わりにpickle
がありますが、デフォルトではlambda
関数のピクルもサポートしていません。ディスパッチテーブルを見てみましょう。
>> import pickle
>> pickle.Pickler.dispatch_table
<member 'dispatch_table' of '_pickle.Pickler' objects>
待つ。探してみましたdispatch_table of pickle
not not _pickle
。 _pickle
はpickleの代替で高速なC実装です。しかし、まだインポートしていません!このC実装は、使用可能な場合、純粋なPython pickle
モジュールの最後に自動的にインポートされます。
# Use the faster _pickle if possible
try:
from _pickle import (
PickleError,
PicklingError,
UnpicklingError,
Pickler,
Unpickler,
dump,
dumps,
load,
loads
)
except ImportError:
Pickler, Unpickler = _Pickler, _Unpickler
dump, dumps, load, loads = _dump, _dumps, _load, _loads
Python 3でラムダを酸洗いする問題が残っています。答えはあなたですCA N'Tネイティブpickle
または_pickle
です。 dill
または cloudpickle をインポートし、ネイティブpickleモジュールの代わりに使用する必要があります。
>> import dill
>> dill.loads(dill.dumps(lambda x:x))
<function __main__.<lambda>>
これですべての疑問が解消されることを願っています。
それは明らかかもしれないが、私は他の可能な解決策を追加したいと思います。ご存知のとおり、ラムダ関数は単なる匿名関数宣言です。一度だけ使用されるラムダが多くなく、コードにノイズを追加しない場合、ラムダに名前を付けて、次のように(括弧なしで)名前を渡すことができます。
import cPickle as pickle
def addition(x, y):
return x+y
if __== "__main__":
s = pickle.dumps(addition)
f = pickle.loads(s)
assert f(3,4) == 7
また、名前はセマンティックを追加するため、Dillのような追加の依存関係は必要ありません。ただし、追加機能の追加ノイズを上回る場合にのみ行ってください。
ディルをインストールする
$ pip install dill
ファイルをタッチする
touch yeah.p
このpython3スクリプトを実行して、
import dill
dill.dump(lambda x:x+1, open('yeah.p', 'wb'))
my_lambda = dill.load(open('yeah.p', 'rb'))
print(my_lambda(2)) # 3