67

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

pickle.PicklingError: Can't pickle <class '__main__.P'>: it's not found as __main__.P

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

这是我的代码:

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)
    with open('abe.pickle', 'wb') as f:
        pickle.dump(abe, f)
    
pickle_test()
4

5 回答 5

91

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

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)
    with open('abe.pickle', 'wb') as f:
        pickle.dump(abe, f)

pickle_test()

现在pickle可以找到它;它现在是一个全局模块。unpickling 时,pickle模块所要做的就是__main__.P再次定位。在您的版本中,P它是函数的本地,pickle_test()并且不可自省或可导入。

重要的是要记住这namedtuple()是一个类工厂;你给它参数,它返回一个类对象供你创建实例。pickle只存储实例中包含的数据,加上对原始类的字符串引用以再次重建实例。

于 2013-05-04T17:51:53.007 回答
10

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

我所做的就是通过有效地将其移动到模块来修补猴子:namedtuple__main__

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

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

于 2013-06-26T20:07:31.967 回答
10

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

group_t =            namedtuple('group_t', 'field1, field2')  # this will work
mismatched_group_t = namedtuple('group_t', 'field1, field2')  # this will throw the error
于 2017-10-12T10:51:32.007 回答
4

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

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")
于 2017-06-12T11:58:14.250 回答
1

这里的问题是子进程无法导入对象的类 - 在这种情况下是类 P-,在多模型项目的情况下,类 P 应该可以在任何使用子进程的地方导入

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

globals()["P"] = P
于 2018-06-11T13:25:29.367 回答