如何在 python 中 pickle 动态创建的嵌套类?

新手上路,请多包涵

我有一个嵌套类:

类小部件类型(对象):

    类浮动类型(对象):
        经过

    类文本类型(对象):
        经过

..和一个引用嵌套类类型(不是它的实例)的对象,就像这样

类 ObjectToPickle(对象):
     def __init__(自我):
         self.type = WidgetType.TextType

尝试序列化 ObjectToPickle 类的实例会导致:

_PicklingError:无法腌制 _

有没有办法在 python 中腌制嵌套类?

原文由 prinzdezibel 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 661
1 个回答

我知道这是一个 非常 古老的问题,但除了重新构建代码的明显且最有可能正确的答案之外,我从未明确地看到过这个问题的令人满意的解决方案。

不幸的是,这样做并不 总是 可行的,在这种情况下,作为最后的手段,可以选择在另一个类中定义的类的实例。

__reduce__ 函数 的 python 文档声明您可以返回

将调用以创建对象的初始版本的可调用对象。元组的下一个元素将为这个可调用对象提供参数。

因此,您所需要的只是一个可以返回适当类实例的对象。这个类本身 必须 是可腌制的(因此,必须存在于 __main__ 级别),并且可以简单如下:

 class _NestedClassGetter(object):
    """
    When called with the containing class as the first argument,
    and the name of the nested class as the second argument,
    returns an instance of the nested class.
    """
    def __call__(self, containing_class, class_name):
        nested_class = getattr(containing_class, class_name)
        # return an instance of a nested_class. Some more intelligence could be
        # applied for class construction if necessary.
        return nested_class()

因此,剩下的就是在 FloatType 的 __reduce__ 方法中返回适当的参数:

 class WidgetType(object):

    class FloatType(object):
        def __reduce__(self):
            # return a class which can return this class when called with the
            # appropriate tuple of arguments
            return (_NestedClassGetter(), (WidgetType, self.__class__.__name__, ))

结果是一个嵌套的类,但实例可以被腌制(需要进一步的工作来转储/加载 __state__ 信息,但根据 __reduce__ 文档,这相对简单)。

同样的技术(稍微修改一下代码)可以应用于深度嵌套的类。

一个完整的例子:

 import pickle

class ParentClass(object):

    class NestedClass(object):
        def __init__(self, var1):
            self.var1 = var1

        def __reduce__(self):
            state = self.__dict__.copy()
            return (_NestedClassGetter(),
                    (ParentClass, self.__class__.__name__, ),
                    state,
                    )

class _NestedClassGetter(object):
    """
    When called with the containing class as the first argument,
    and the name of the nested class as the second argument,
    returns an instance of the nested class.
    """
    def __call__(self, containing_class, class_name):
        nested_class = getattr(containing_class, class_name)

        # make an instance of a simple object (this one will do), for which we can change the
        # __class__ later on.
        nested_instance = _NestedClassGetter()

        # set the class of the instance, the __init__ will never be called on the class
        # but the original state will be set later on by pickle.
        nested_instance.__class__ = nested_class
        return nested_instance

if __name__ == '__main__':

    orig = ParentClass.NestedClass(var1=['hello', 'world'])

    pickle.dump(orig, open('simple.pickle', 'w'))

    pickled = pickle.load(open('simple.pickle', 'r'))

    print type(pickled)
    print pickled.var1

我对此的最后一点是记住其他答案所说的话:

如果您有能力这样做,请考虑重构您的代码以首先避免嵌套类。

原文由 pelson 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题