【问题标题】:Can Python pickle lambda functions?Python可以腌制lambda函数吗?
【发布时间】:2014-10-10 11:52:01
【问题描述】:

我在许多线程中读到 Python pickle/cPickle 不能腌制 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

那么发生了什么?或者更确切地说,酸洗 lambda 的极限是多少?

[编辑] 我想我知道为什么这段代码会运行。我忘记了(对不起!)我正在运行无堆栈 python,它有一种称为 tasklet 的微线程形式来执行一个函数。这些 tasklet 可以暂停、腌制、取消腌制和继续,所以我猜(在无堆栈邮件列表中询问)它还提供了一种腌制函数体的方法。

【问题讨论】:

  • 无法复制 - 在 2.7.6 中(在 OS X 10.9.4 上)我得到 TypeError: can't pickle function objects
  • 从其他脚本尝试pickle.loads;我认为你腌制对 lambda 的引用,在同一范围内,它被保存在内存中并被调用。
  • 只是想知道,您使用的是哪个版本的 cPickle?
  • @Lars: stacklessdill 所做的完全一样……主要区别在于stackless 替换了C 中的调用堆栈,而dill 尝试使用以下方法注册序列化函数ctypes 尽可能在 C 层工作。 Stackless 可以序列化所有对象。

标签: python python-2.7 lambda pickle


【解决方案1】:

是的,python 可以腌制 lambda 函数……但前提是你有使用 copy_reg 来注册 如何 腌制 lambda 函数的东西 -- dill 包会加载你需要的 copy_reg当你 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>

在这里获取莳萝:https://github.com/uqfoundation

【讨论】:

  • 我在 python3 上试过 [1]: import dill In [2]: import pickle In [3]: pickle.dumps(lambda x: (x+1, x+2)) -------------------------------------------------- ------------------------- PicklingError Traceback (最近一次调用最后一次) in () -- --> 1 pickle.dumps(lambda x: (x+1, x+2)) PicklingError: Can't pickle at 0x7f08ee40ca60>: attribute lookup on main失败的。仅当您将莳萝作为泡菜导入时才有效
  • @Ramast:你是​​对的——在 python3 中,你目前必须import dill as pickle。在 python2 中,我上面的任何一种方式都可以。
  • @CharlieParker:你能详细说明一下吗?以上应该适用于大多数“任意”功能。
  • 对于 Python 3.6 的答案是不正确的 - 请改用 dill.dumps()dill.loads()
  • @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
【解决方案2】:

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

  • Python 2.7

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 模块来注册我们的函数。你可以阅读更多关于它的信息here。默认情况下,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: &lt;function pickle.save_global&gt; 无法序列化 lambda 函数。因此,dillcloudpickle 等所有第三方库都会覆盖内置方法,以使用一些额外的逻辑来序列化 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>}

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

>> pickle.loads(pickle.dumps(lambda x:x))
<function __main__.<lambda>>

它有效!

在 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

现在,让我们尝试使用 C 实现 cPickle 腌制 lambda。

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

等等。我尝试查找 pickledispatch_table 而不是 _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 中酸洗 lambda 的问题。答案是你不能使用原生的 pickle_pickle。您将需要导入 dillcloudpickle 并使用它来代替本机 pickle 模块。

>> import dill
>> dill.loads(dill.dumps(lambda x:x))
<function __main__.<lambda>>

我希望这能消除所有疑虑。

【讨论】:

  • 这个答案应该被接受。它很好地解释了每个 python 版本中 dill 包的可用性和局限性。干得好!
【解决方案3】:

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

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

【讨论】:

  • 我不知道为什么这条评论被否决了。 pickle 不能序列化 lambdas 只有 dill 包可以
  • 为什么 python pickle lambdas 不能?
【解决方案4】:

尽管可能很明显,但我想添加其他可能的解决方案。 您可能知道 lambda 函数只是匿名函数声明。如果您没有很多只使用一次的 lambda,并且它不会给您的代码增加太多噪音,您可以命名您的 lambda 并传递它的名称(不带括号),如下所示:

import cPickle as pickle

def addition(x, y):
    return x+y


if __name__ == "__main__":
    s = pickle.dumps(addition)
    f = pickle.loads(s)
    assert f(3,4) == 7

该名称还增加了更多语义,您不需要像 Dill 这样的额外依赖项。但只有当它超过附加功能的额外噪音时才这样做。

【讨论】:

    【解决方案5】:

    对我有用的(Windows 10、python 3.7)是传递一个函数而不是 lambda 函数:

    def merge(x):
        return Image.merge("RGB", x.split()[::-1])
    
    transforms.Lambda(merge)
    

    代替:

    transforms.Lambda(lambda x: Image.merge("RGB", x.split()[::-1]))
    

    不需要 dill 或 cPickel。

    【讨论】:

      【解决方案6】:

      安装莳萝

      $ 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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-08-14
        • 2013-01-10
        • 1970-01-01
        • 1970-01-01
        • 2016-01-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多