【问题标题】:Python: pickling nested functionsPython:酸洗嵌套函数
【发布时间】:2012-08-18 15:51:03
【问题描述】:

使用示例

def foo(a):
    def bar(b):
        return a+b
    return bar

d = {1:foo(1), 2:foo(2)}

pickle 模块似乎无法与未在模块范围内定义的函数一起使用,因此 pickle 'd' 将不起作用。我应该考虑另一种酸洗机制吗?

【问题讨论】:

  • @Maxim Khesin 我修复了其他人指出的错误,因为您的意思似乎很明显。如果我的编辑有误,请骂我。

标签: python function nested pickle


【解决方案1】:

恐怕你不能腌制嵌套函数。

pickle 模块按名称序列化函数。也就是说,如果您在模块mymodule 中有一个函数myfunc,它只会保存名称mymodule.myfunc,并在反序列化时再次查找它。 (这是一个重要的安全性和兼容性问题,因为它保证反序列化代码使用它自己的函数定义,而不是可能被破坏或过时的原始定义。)

唉,pickle 不能用嵌套函数做到这一点,因为没有办法直接通过名称来寻址它们。例如,您的 bar 函数无法从 foo 外部访问。

如果您需要一个像函数一样工作的可序列化对象,您可以改为使用__call__ 方法创建一个类:

class foo(object):
    def __init__(self, a):
        self.a = a
    def __call__(self, b): # the function formerly known as "bar"
        return self.a + b

这就像问题中的嵌套函数一样,应该不会对pickle 造成问题。但请注意,当您反序列化 foo 实例时,您需要有相同的类定义可用。

【讨论】:

  • 应该是class foo(object) 而不是class foo(a)
  • @PanChao:嗯,我认为你是对的,这是一个错字。我会解决的。
  • 它带来的真正好处是,它迫使您构建代码
【解决方案2】:

如果您使用dill 而不是pickle,则可以腌制嵌套函数。

>>> import dill
>>>    
>>> def foo(a):
...   def bar(b):
...     return a+b
...   return bar
... 
>>> d = {1:foo(1), 2:foo(2)}
>>> 
>>> _d = dill.dumps(d)
>>> d_ = dill.loads(_d)
>>> d_
{1: <function bar at 0x108cfe848>, 2: <function bar at 0x108cfe8c0>}
>>> d[1](0) + d[2](10)
13
>>> 

【讨论】:

  • 正确答案!非常感谢,迈克。
  • 我尝试将这个确切的代码粘贴到我的 python 脚本中,但我得到 TypeError: cannot pickle 'sqlite3.Connection' object
  • @skrhee:我认为您的环境正在发生一些额外的事情。在此处查看我对您的帖子的评论:github.com/uqfoundation/dill/issues/435
【解决方案3】:

根据 Blckknght 的回答。如果嵌套函数是唯一的外部序列化类型并将其用作decorator,则只需在内部函数定义顶部添加functools.warps,以引导其他解释找到正确的名称:

from functools import warps

def foo(func):
    @wraps(func)
    def bar(b):
        return func(b)
    return bar

@foo
def zzz(b):
    return b

【讨论】:

    猜你喜欢
    • 2013-05-13
    • 2021-11-01
    • 1970-01-01
    • 2012-05-03
    • 2011-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多