【问题标题】:How to pickle a namedtuple instance correctly如何正确腌制命名元组实例
【发布时间】:2013-04-28 22:30:04
【问题描述】:

我正在学习如何使用泡菜。我创建了一个 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)
    f = open('abe.pickle', 'w')
    pickle.dump(abe, f)
    f.close()

pickle_test()

【问题讨论】:

  • 不幸的是,pickle 似乎不适用于命名元组。
  • @Antimony: pickle 处理 namedtuple 类就好了;在函数本地命名空间中定义的类不多。
  • @AirThomas 这个问题是在一年前提出/回答的:)
  • 未拍摄。我只是觉得这很有趣。问题链接确实非常有用:)

标签: python python-2.7 pickle namedtuple


【解决方案1】:

在我将我的问题作为对主要答案的评论添加后,我找到了一种方法来解决制作动态创建的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__ 中的另一个成员。

【讨论】:

  • 只需将其设置在globals() 上:globals()[namedtupleClass.__name__] = namedtupleClass。那么就不需要设置__module__了。
  • 当我尝试globals()[namedtupleClass.__name__] = namedtupleClass 时,它确实允许我腌制我的对象,但是当我尝试取消腌制时,它没有所需的namedtupleClass。我的建议是只使用字典,直到他们让泡菜聪明到可以做到这一点。
【解决方案2】:

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

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

globals()["P"] = P

【讨论】:

    【解决方案3】:

    我在另一个帖子中找到了this answer。这都是关于命名元组的命名。这对我有用:

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

    【讨论】:

      【解决方案4】:

      或者,您可以使用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")
      

      【讨论】:

        【解决方案5】:

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

        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可以找到了;它现在是一个全局模块。当 unpickling 时,所有pickle 模块所要做的就是再次定位__main__.P。在您的版本中,Ppickle_test() 函数的本地,它是不可自省或可导入的。

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

        【讨论】:

        • 那么,如果我动态创建namedtuple,因为直到运行时我才知道这些字段怎么办?还有办法绕过这个问题吗?我尝试在课堂之外创建另一种方法,但没有奏效。
        • @Chuim:将其分配给 同名 下的模块全局变量(使用globals() 获取映射),pickle 仍然可以找到它。跨度>
        • 如果您正在创建多个具有动态字段的命名元组,则在 globals 中覆盖 P 后,unpickling 将不起作用。
        • @DavitTovmasyan:不,每个类都需要一个单独的名称。
        • 它似乎不适用于 Python 3.x
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-03-30
        • 1970-01-01
        • 2018-02-04
        • 1970-01-01
        • 2013-06-15
        相关资源
        最近更新 更多