Python可以腌制lambda函数吗?

Lar*_*ars 48 python lambda pickle python-2.7

我已经阅读了许多线程,Python pickle/ cPickle不能pickle lambda函数.但是,以下代码使用Python 2.7.6:

import cPickle as pickle

if __name__ == "__main__":
    s = pickle.dumps(lambda x, y: x+y)
    f = pickle.loads(s)
    assert f(3,4) == 7
Run Code Online (Sandbox Code Playgroud)

那么发生了什么?或者说,酸洗lambdas的限制是什么?

[编辑]我想我知道为什么这个代码运行.我忘了(抱歉!)我正在运行无堆栈python,它有一种称为tasklet执行函数的微线程形式.这些tasklet可以暂停,腌制,打开并继续,所以我猜(在无堆栈邮件列表上询问)它还提供了一种方法来挑选函数体.

Mik*_*rns 70

是的,python可以腌制lambda函数......但只有当你有一些copy_reg用于注册如何 pickle lambda函数的东西时 - 当你运行时,软件包dill会将copy_reg你需要的东西加载到pickle注册表中import dill.

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>
Run Code Online (Sandbox Code Playgroud)

在这里得到莳萝:https://github.com/uqfoundation

  • @Ramast:你是​​对的 - 在python3中,你现在必须`导入莳萝作为pickle`.在python2中,我所拥有的任何一种方式都可以. (13认同)
  • 我试过python3 In [1]:import dill In [2]:import pickle In [3]:pickle.dumps(lambda x:(x + 1,x + 2))-------- -------------------------------------------------- ----------------- PicklingError Traceback(最近一次调用最后一次)<module>()---->中的ipython-input-3-924e2f4cc7e0> 1 pickle.dumps( lambda x:(x + 1,x + 2))PicklingError:无法在0x7f08ee40ca60>上选择<function <lambda>:__ main__上的属性查找<lambda>失败.它只适用于将莳萝导入为泡菜 (4认同)
  • @CiprianTomoiagă:主要区别在于,在python 2中,有`pickle`(python)和`cPickle`(C)——而在python 3中,现在是`pickle`(python)和`_pickle`(C)。 .. 然而,`pickle.dump`(和`dumps`)使用`_pickle`(即C)......而`dill` 目前只能将新方法注入python pickle 注册表。因此,仅导入 `dill` 不像在 python 2 中那样工作。请注意,有 `pickle._dump`(和 `_dumps`),它确实使用了 python 注册表......所以它像在 python 2 中一样工作. 不幸的是,当 `dump` 失败时,大多数包不会回退到 `_dump`。 (3认同)
  • @CharlieParker:你能详细说明一下吗?以上应该适用于大多数“任意”功能。 (2认同)
  • Python 3.6 的答案是不正确的 - 使用 `dill.dumps()` 和 `dill.loads()` 代替。 (2认同)

Ned*_*der 31

不,Python不能腌制lambda函数:

>>> 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
Run Code Online (Sandbox Code Playgroud)

不确定你做了什么成功...

  • 我不知道为什么这个评论被拒绝投票.pickle不能序列化lambdas只有dill包才可以 (6认同)
  • 为什么python pickle lambdas不能? (3认同)

Sai*_*aza 16

Python可以腌制lambdas.我们将分别介绍Python 2和3,因为pickle的实现在不同的Python版本中是不同的.

  • Python 2.7

pickle使用pickle注册表,它只是从type用于序列化(酸洗)该类型对象的函数的映射.您可以看到pickle注册表:

>> 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>}
Run Code Online (Sandbox Code Playgroud)

为了挑选自定义类型,Python提供copy_reg了注册我们函数的模块.你可以在这里阅读更多相关信息.默认情况下,copy_reg模块支持以下其他类型的pickle:

>> 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>}
Run Code Online (Sandbox Code Playgroud)

现在,lambda函数的类型是types.FunctionType.但是,此类型的内置函数function: <function pickle.save_global>无法序列化lambda函数.因此,所有第三方库(如dill,cloudpickle等)都会覆盖内置方法,以使用一些额外的逻辑来序列化lambda函数.让我们导入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>}
Run Code Online (Sandbox Code Playgroud)

现在,让我们尝试腌制lambda函数.

>> pickle.loads(pickle.dumps(lambda x:x))
<function __main__.<lambda>>
Run Code Online (Sandbox Code Playgroud)

有用!!

在Python 2中我们有两个版本pickle-

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
Run Code Online (Sandbox Code Playgroud)

现在,让我们尝试用C实现pickle lambda cPickle.

>> import cPickle
>> cPickle.loads(cPickle.dumps(lambda x:x))
TypeError: can't pickle function objects
Run Code Online (Sandbox Code Playgroud)

什么地方出了错?我们来看看调度表cPickle.

>> cPickle.Pickler.dispatch_table
AttributeError: 'builtin_function_or_method' object has no attribute 'dispatch_table'
Run Code Online (Sandbox Code Playgroud)

执行picklecPickle不同.Importingdill只做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>
Run Code Online (Sandbox Code Playgroud)

等待.我试图查找dispatch_tablepickle不是_pickle._pickle是pickle的替代和更快的C实现.但是我们还没有进口它!如果可用,则在纯Python pickle模块的末尾自动导入此C实现.

# 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
Run Code Online (Sandbox Code Playgroud)

我们仍然留下在Python 3中腌制lambdas的问题.答案是你不能用本机pickle或者_pickle.您将需要导入dillcloudpickle并使用它而不是原生pickle模块.

>> import dill
>> dill.loads(dill.dumps(lambda x:x))
<function __main__.<lambda>>
Run Code Online (Sandbox Code Playgroud)

我希望这能解决所有疑虑.

  • 这个答案应该被接受。它很好地解释了每个 python 版本中“dill”包的可用性和限制。做得好! (3认同)