Python 如何正确地pickle namedtuple实例

Python 如何正确地pickle namedtuple实例,python,python-2.7,pickle,namedtuple,Python,Python 2.7,Pickle,Namedtuple,我正在学习如何使用泡菜。我创建了一个namedtuple对象,将其附加到列表中,并尝试对该列表进行pickle。但是,我得到以下错误: pickle.PicklingError: Can't pickle <class '__main__.P'>: it's not found as __main__.P 在函数外部创建命名元组: from collections import namedtuple import pickle P = namedtuple("P", "one t

我正在学习如何使用泡菜。我创建了一个namedtuple对象,将其附加到列表中,并尝试对该列表进行pickle。但是,我得到以下错误:

pickle.PicklingError: Can't pickle <class '__main__.P'>: it's not found as __main__.P
在函数外部创建命名元组:

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()
现在
pickle
可以找到它;它现在是一个全球模块。取消勾选时,
pickle
模块需要做的就是再次定位
\uuuu main\uuu.P
。在您的版本中,
P
pickle\u test()
函数的本地函数,它不可内省或导入


重要的是要记住,
namedtuple()
是一个类工厂;您为它提供参数,它将返回一个类对象,供您从中创建实例<代码>pickle只存储实例中包含的数据,加上对原始类的字符串引用以再次重构实例。

在我将问题作为注释添加到主要答案后,我找到了一种方法来解决动态创建的
namedtuple
pickle的问题。这在我的例子中是必需的,因为我只在运行时(在DB查询之后)计算它的字段

我所做的就是通过有效地将
\uuuu主模块
移动到
模块,对
命名双工
进行猴子补丁:

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

请注意,如果您不小心,
namedtuple
名称(由
args
提供)可能会覆盖
\uuuuu main\uuuu
中的另一个成员。

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

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")
我在另一个线程中发现。这都是关于命名元组的命名。这对我很有用:

group_t =            namedtuple('group_t', 'field1, field2')  # this will work
mismatched_group_t = namedtuple('group_t', 'field1, field2')  # this will throw the error

这里的问题是,子进程无法导入对象的类(在本例中为类p),在多模型项目中,类p应该可以在子进程使用的任何位置导入

一个快速的解决方法是通过将其影响到globals()使其可导入


不幸的是,pickle似乎不能很好地处理namedtuple。@锑:
pickle
可以很好地处理namedtuple类;在函数本地名称空间中定义的类不多。可能与@AirThomas重复此问题是在一年前提出/回答的:)无需回答。我只是觉得很有趣。问题链接确实非常有用:)那么,如果我动态创建
namedtuple
,因为我直到运行时才知道字段,该怎么办?还有办法绕过这个问题吗?我尝试在类之外创建另一个方法,但没有成功。@Chuim:将其分配给同名的模块全局(使用
globals()
获取映射),并且
pickle
仍然可以找到它。如果您正在创建带动态字段的多个namedtuple,在覆盖
globals
@DavitTovmasyan中的
P
后,取消勾选将不起作用:不,每个类都需要一个单独的名称。只需将其设置为
globals()
即可:
globals()[namedtupleClass.\uu name\uuuu]=namedtupleClass
。当我尝试
globals()[namedtupleClass.\uu name\uuu]=namedtupleClass
时,它确实允许我对对象进行pickle,但当我尝试取消pickle时,它没有所需的
namedtupleClass
。我的建议是使用字典,直到他们把pickle弄得足够聪明,可以这样做。
group_t =            namedtuple('group_t', 'field1, field2')  # this will work
mismatched_group_t = namedtuple('group_t', 'field1, field2')  # this will throw the error
globals()["P"] = P