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

如何在python中pickle动态创建的嵌套类?,python,class,nested,pickle,Python,Class,Nested,Pickle,我有一个嵌套类: class WidgetType(object): class FloatType(object): pass class TextType(object): pass 类WidgetType(对象): 类类型(对象): 通过 类TextType(对象): 通过 。。和一个引用嵌套类类型(不是它的实例)的对象,如下所示 class ObjectToPickle(object): def __init_

我有一个嵌套类:

class WidgetType(object): class FloatType(object): pass class TextType(object): pass 类WidgetType(对象): 类类型(对象): 通过 类TextType(对象): 通过 。。和一个引用嵌套类类型(不是它的实例)的对象,如下所示

class ObjectToPickle(object): def __init__(self): self.type = WidgetType.TextType 类ObjectToPickle(对象): 定义初始化(自): self.type=WidgetType.TextType 尝试序列化ObjectToPickle类的实例会导致:

PicklingError:不能腌制


有没有办法在python中pickle嵌套类?

pickle仅适用于在模块范围(顶级)中定义的类。在这种情况下,您似乎可以在模块范围内定义嵌套类,然后将它们设置为WidgetType上的属性,假设有理由不在代码中仅引用
TextType
FloatType
。或者,导入它们所在的模块,并使用
小部件\u-type.TextType
小部件\u-type.FloatType

pickle模块正在尝试从该模块获取TextType类。但是由于类是嵌套的,所以它不起作用。jasonjs的建议会奏效。 以下是pickle.py中负责错误消息的行:

    try:
        __import__(module)
        mod = sys.modules[module]
        klass = getattr(mod, name)
    except (ImportError, KeyError, AttributeError):
        raise PicklingError(
            "Can't pickle %r: it's not found as %s.%s" %
            (obj, module, name))
klass=getattr(mod,name)
当然不会在嵌套类中工作。要演示正在进行的操作,请在酸洗实例之前尝试添加以下行:

import sys
setattr(sys.modules[__name__], 'TextType', WidgetType.TextType)

此代码将TextType作为属性添加到模块中。酸洗应该很好用。不过,我不建议你使用这种方法。

娜迪亚的回答相当完整——实际上这不是你想做的事情;您确定不能在
WidgetTypes
中使用继承而不是嵌套类吗


使用嵌套类的唯一原因是封装紧密协作的类,您的特定示例对我来说就像一个直接的继承候选者-将
widgetype
类嵌套在一起没有任何好处;将它们放在一个模块中,并从基
WidgetType
继承。

在Sage()中,我们有许多这种酸洗问题的实例。我们决定系统地解决这个问题的方法是将外部类放在一个特定的元类中,该元类的目标是实现和隐藏黑客行为。请注意,如果存在多个嵌套级别,这会自动通过嵌套类传播。

我知道这是一个非常古老的问题,但我从来没有明确看到过这个问题的满意解决方案,除了重新构造代码的明显且很可能是正确的答案

不幸的是,这样做并不总是切实可行的,在这种情况下,作为最后的手段,可以对在另一个类中定义的类的实例进行pickle处理

可以返回的状态的python文档

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

因此,您所需要的只是一个可以返回相应类的实例的对象。此类本身必须是可拾取的(因此,必须位于
\uuuuuu main\uuuuuuu
级别),可以简单到:

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上的
\uuuuu reduce\uuuu
方法中返回适当的参数:

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__, ))
结果是一个嵌套的类,但可以对实例进行pickle(转储/加载
\uuuu状态\uuuu
信息需要进一步的工作,但根据
\uuu减少\uuuu
文档,这相对简单)

同样的技术(稍加代码修改)也可以应用于深度嵌套的类

一个充分发挥作用的例子:

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
关于这一点,我的最后一点是要记住其他答案所说的话:

如果你有这样的位置,考虑重构你的代码。 首先要避免嵌套类


如果您使用
dill
而不是
pickle
,它会起作用

>>> import dill
>>> 
>>> class WidgetType(object):
...   class FloatType(object):
...     pass
...   class TextType(object):
...     pass
... 
>>> class ObjectToPickle(object):
...   def __init__(self):
...     self.type = WidgetType.TextType
... 
>>> x = ObjectToPickle()
>>> 
>>> _x = dill.dumps(x)
>>> x_ = dill.loads(_x)
>>> x_
<__main__.ObjectToPickle object at 0x10b20a250>
>>> x_.type
<class '__main__.TextType'>
>>导入莳萝
>>> 
>>>类WidgetType(对象):
...   类类型(对象):
...     通过
...   类TextType(对象):
...     通过
... 
>>>类ObjectToPickle(对象):
...   定义初始化(自):
...     self.type=WidgetType.TextType
... 
>>>x=ObjectToPickle()
>>> 
>>>_x=模拟转储(x)
>>>x_ux=静载荷(x)
>>>x_
>>>x型

在这里找到迪尔:

hehe,你的用户名是巧合吗pse还可以查看Python pickle文档:在Python 3.4及更高版本中,
inst=ObjectToPickle();pickle.dumps(inst,4)
工作正常。[..]将它们设置为WidgetType上的属性[…]这样做会产生:“TypeError:无法pickle属性对象”我遇到这个问题,因为我使用
cPickle
hashlib.sha1()
来获取给定对象实例的哈希值。通过使用第三方模块,我遇到了从libpcap文件解析ICMP数据的需要,并发现dpkt在
ICMP()
ICMP6()
中都使用嵌套类。更改dpkt的icmp.py中的代码可以运行,但这在我无法控制的其他系统上不是一个实用的解决方案。因此,您的答案虽然合理,但并不适用于所有情况,因为其他人将如何编写他们的代码。遇到这种情况,看来现在泡菜不好用。还发现了你的终端进度条,它真的很整洁!将模块末尾的内部类名命名为
TextType=WidgetType.TextType
稍微简单一点,尽管它并没有让它变得更简单,而且它仍然高喊“删除内部类”。我没有找到有关Dill在不同名称空间中处理pickle对象的信息(参见问题)?
dill
可以通过引用或类定义来pickle类(也就是说,它可以pickle类源代码)。更多的细化或示例会有所帮助。要在Python 3中工作,请将
w
替换为
wb
以及