如何正确地pickle一个namedtuple实例

Dir*_*uin 45 python pickle namedtuple python-2.7

我正在学习如何使用泡菜.我创建了一个namedtuple对象,将其附加到列表中,并试图挑选该列表.但是,我收到以下错误:

pickle.PicklingError: Can't pickle <class '__main__.P'>: it's not found as __main__.P
Run Code Online (Sandbox Code Playgroud)

我发现,如果我运行代码而不将其包装在函数中,它就可以完美地运行.在函数内部包装时,是否需要额外的步骤来腌制对象?

这是我的代码:

from collections import namedtuple
import pickle

def pickle_test():
    P = namedtuple("P", "one two three four")
    my_list = []
    abe = P("abraham", "lincoln", "vampire", "hunter")
    my_list.append(abe)
    f = open('abe.pickle', 'w')
    pickle.dump(abe, f)
    f.close()

pickle_test()
Run Code Online (Sandbox Code Playgroud)

Mar*_*ers 68

在函数外部创建命名元组:

from collections import namedtuple
import pickle

P = namedtuple("P", "one two three four")

def pickle_test():
    my_list = []
    abe = P("abraham", "lincoln", "vampire", "hunter")
    my_list.append(abe)
    f = open('abe.pickle', 'w')
    pickle.dump(abe, f)
    f.close()

pickle_test()
Run Code Online (Sandbox Code Playgroud)

现在pickle可以找到它; 它现在是一个全球模块.当拆开时,所有pickle模块要做的就是__main__.P再次定位.在您的版本中,P本地的,pickle_test()功能的,并且不是内省的或可导入的.

重要的是要记住,这namedtuple()是一个班级工厂; 你给它参数,它返回一个类对象,你可以从中创建实例.pickle仅存储实例中包含的数据,以及对原始类的字符串引用,以再次重构实例.

  • 那么,如果我动态创建`namedtuple`会怎么样,因为直到运行时才知道字段?还有办法绕过这个问题吗?我尝试在类之外创建另一个方法但是没有用. (7认同)
  • @Chuim:将它分配给你的模块全局变量(使用`globals()`获取映射)在*同名*下,`pickle`可以找到它. (7认同)

Chu*_*uim 8

在我将问题添加为对主要答案的评论之后,我找到了一种方法来解决制作动态创建的namedtuplepickle-able的问题.这在我的情况下是必需的,因为我只在运行时(在数据库查询之后)确定其字段.

我要做的就是猴子打补丁namedtuple通过有效地将其移动到__main__模块:

def _CreateNamedOnMain(*args):
    import __main__
    namedtupleClass = collections.namedtuple(*args)
    setattr(__main__, namedtupleClass.__name__, namedtupleClass)
    namedtupleClass.__module__ = "__main__"
    return namedtupleClass
Run Code Online (Sandbox Code Playgroud)

请注意,如果您不小心,namedtuple名称(由其提供args)可能会覆盖其他成员__main__.

  • 只需将它设置在`globals()`上:`globals()[namedtupleClass .__ name__] = namedtupleClass`.然后*不需要*设置`__module__`. (15认同)

Peq*_*que 6

或者,您可以使用cloudpickledill进行序列化:

from collections import namedtuple

import cloudpickle
import dill



def dill_test(dynamic_names):
    P = namedtuple('P', dynamic_names)
    my_list = []
    abe = P("abraham", "lincoln", "vampire", "hunter")
    my_list.append(abe)
    with open('deleteme.cloudpickle', 'wb') as f:
        cloudpickle.dump(abe, f)
    with open('deleteme.dill', 'wb') as f:
        dill.dump(abe, f)


dill_test("one two three four")
Run Code Online (Sandbox Code Playgroud)


Ruv*_*aba 5

我在另一个线程中找到了这个答案。这就是命名元组的命名。这对我有用:

group_t =            namedtuple('group_t', 'field1, field2')  # this will work
mismatched_group_t = namedtuple('group_t', 'field1, field2')  # this will throw the error
Run Code Online (Sandbox Code Playgroud)