【问题标题】:Pickle all attributes except one腌制除一个以外的所有属性
【发布时间】:2011-10-01 21:29:56
【问题描述】:

编写__getstate__ 方法的最佳方法是腌制几乎对象的所有属性,但排除一些属性?

我有一个具有许多属性的对象,包括一个引用实例方法的对象。 instancemethod 不可腌制,因此当我尝试腌制此对象时出现错误:

class Foo(object):
    def __init__(self):
        self.a = 'spam'
        self.b = 'eggs'
        self.c = 42
        self.fn = self.my_func
    def my_func(self):
        print 'My hovercraft is full of eels'

import pickle
pickle.dumps(Foo())              # throws a "can't pickle instancemethod objects" TypeError

这个__getstate__ 方法解决了这个问题,但是我必须手动包含我想要序列化的所有属性:

def __getstate__(self):
    return { 'a': self.a, 'b': self.b, 'c': self.c }

如果我有一个具有许多属性或经常更改的对象,那么这不是非常可扩展或可维护的。

我能想到的唯一替代方法是某种辅助函数,它遍历对象的属性并根据类型将它们添加(或不添加)到字典中。

【问题讨论】:

  • +1 供气垫船参考(此外,这是一个有价值的问题;)

标签: python pickle


【解决方案1】:

我能想到的唯一替代方法是某种辅助函数,它遍历对象的属性并根据类型将它们添加(或不添加)到字典中。

是的,如果您想要足够的“魔法”来让自己变得懒惰(和/或允许动态添加属性),我认为这几乎就是您所剩下的。请记住,“pickle 无法处理此问题”并不是您不想在腌制状态中包含某些内容的唯一原因。

但这并不像你想象的那么难,假设你有“我应该腌制这个吗?”的代码。逻辑:

def __getstate__(self):
  return {k:v for (k, v) in self.__dict__.items() if should_pickle(v)}

【讨论】:

  • 父类呢?您的 getstate 函数会为父属性做正确的事情吗?
  • dict comprehension 在这里稍微整洁一些:{k: v for k, v in self.__dict__.iteritems() if should_pickle(v)}
  • 和@Venza:我认为它会很好地处理继承。 (但它不会包含任何类属性。)
  • 已更新以使代码现代化,因为 OP 并没有被标记为 2.x。
【解决方案2】:

使用较早答案中的is_instance_method

def __getstate__(self):
    return dict((k, v) for k, v in self.__dict__.iteritems()
                       if not is_instance_method(getattr(self, k)))

虽然 is_instance_method 操作也可以通过采用已知的实例方法(例如 my_func)并采用其类型来执行,但不会那么“神奇”。

def __getstate__(self):
    instancemethod = type(self.my_func)
    return dict((k, v) for k, v in self.__dict__.iteritems()
                       if not isinstance(getattr(self, k), instancemethod))

【讨论】:

    【解决方案3】:

    你总是可以删除坏项目:

    def __getstate__(self):
        state = self.__dict__
        del state[...]
        return state
    

    【讨论】:

    • 但这会修改​​原始对象,这是我不想要的。我想我可以对self.__dict__ 进行深度复制并从副本中删除属性。不过,这可能会带来其他问题。
    • @Mike:哎呀,忘记复制字典了。是的,目的是先复制它。
    • @Mike:注意你只需要一个浅拷贝,因为你从不修改键和值(只修改字典本身)。
    • .copy() 编辑。然后我认为这是对原始问题的优雅回答,该问题侧重于名称已知的单个或几个属性。那些带有列表/字典理解和昂贵类型检查的答案是不必要的痛苦。
    【解决方案4】:

    我会切入问题的根源,并首先尝试序列化所谓的“不可腌制”项目。 为此,我会使用dill,它可以序列化python 中的几乎所有内容。 Dill 也有 some good tools 帮助您了解在代码失败时导致酸洗失败的原因。

    >>> import dill
    >>> dill.loads(dill.dumps(your_bad_object))
    >>> ...
    >>> # if you get a pickling error, use dill's tools to figure out a workaround
    >>> dill.detect.badobjects(your_bad_object, depth=0)
    >>> dill.detect.badobjects(your_bad_object, depth=1)
    >>> ...
    

    如果您绝对愿意,您可以使用 dill 的 badobjects(或其他检测函数之一)递归地深入到对象的引用链中,并弹出不可腌制的对象,而不是在每个深度调用它,如上。

    【讨论】:

      【解决方案5】:

      __slots__解决方案

      如果您使用插槽,则可以避免重复成员排除:

      class C(object):
          _pickle_slots = ['i']
          __slots__ = _pickle_slots + ['j']
          def __init__(self, i, j):
              self.i = i
              self.j = j
          def __getstate__(self):
              return (None, {k:getattr(self, k) for k in C._pickle_slots })
      
      o = pickle.loads(pickle.dumps(C(1, 2), -1))
      
      # i is there
      assert o.i == 1
      
      # j was excluded
      try:
          o.j
      except:
          pass
      else:
          raise
      

      在 Python 2.7.6 中测试。

      【讨论】:

        【解决方案6】:

        对于您的具体情况(防止函数被腌制),请使用:

        self.__class__.fn = self.__class__.my_func

        现在,不是将函数添加到类的实例,而是将其添加到类本身,因此该函数不会被腌制。如果您希望每个实例都有自己的 fn 版本,这将不起作用。

        我的场景是我想有选择地将get_absolute_url 添加到一些 Django 模型中,并且我想在抽象的BaseModel 类中定义它。我有self.get_absolute_url = … 并遇到了pickle 问题。刚刚将__class__ 添加到作业中解决了我的问题。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-03-19
          • 1970-01-01
          • 2017-12-26
          • 2022-06-26
          • 1970-01-01
          • 2018-05-24
          相关资源
          最近更新 更多