web-dev-qa-db-ja.com

Python pickle lambda functions?

私は多くのスレッドで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を実行しています。これらのタスクレットは停止、ピクル、アンピクル、および継続することができるため、関数本体をピクルする方法も提供していると推測されます(スタックレスメーリングリストで質問)。

47
Lars

はい、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

67
Mike McKerns

いいえ、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

成功したかわからない...

31
Ned Batchelder

Pythonはラムダをピクルできます。 Pythonバージョンによってpickleの実装が異なるため、Python 2と3を別々に扱います。

  • Python 2.7

picklepickle 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_regmoduleは次の追加タイプの酸洗いをサポートしています。

>> 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>は、ラムダ関数をシリアル化できません。したがって、dillcloudpickleなどのすべてのサードパーティライブラリは、組み込みメソッドをオーバーライドして、いくつかの追加ロジックでラムダ関数をシリアル化します。 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'

picklecPickleの実装は異なります。 Importing dillはPythonバージョンのpickleのみを機能させます。 pickleの代わりにcPickleを使用することの欠点は、cPickleよりも1000回遅くなる可能性があることです。

  • Python 3.6

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>>

これですべての疑問が解消されることを願っています。

14
Saim Raza

それは明らかかもしれないが、私は他の可能な解決策を追加したいと思います。ご存知のとおり、ラムダ関数は単なる匿名関数宣言です。一度だけ使用されるラムダが多くなく、コードにノイズを追加しない場合、ラムダに名前を付けて、次のように(括弧なしで)名前を渡すことができます。

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のような追加の依存関係は必要ありません。ただし、追加機能の追加ノイズを上回る場合にのみ行ってください。

0
Sandro

ディルをインストールする

$ 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
0
juan Isaza