4

我正在尝试将动态生成的类腌制为替代类的工厂。类似于以下内容:

import sys, pickle

class BC(object):
    pass

C = type("NewClassName", (BC,), {})

pickle.dump(C, sys.stdout)

这会导致以下错误:

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

要腌制动态生成的类的对象,您可以定义一个__reduce__方法,但是有没有办法仅针对类定义来实现这一点。

我不想直接使用 BC,因为我只需要它作为新类的工厂。

4

3 回答 3

2

您可以使用dill,它可以序列化动态类定义。然后你不需要任何变通方法,你可以做你想做的事。

Python 2.7.7 (default, Jun  2 2014, 01:33:50) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>>>
>>> class BC(object):
...   pass
... 
>>> c = type("NewClassName", (BC,), {})
>>> _c = dill.dumps(c)    
>>> c2 = dill.loads(_c)
>>> c2
<class '__main__.NewClassName'>
>>> 

dill到这里:https : //github.com/uqfoundation

于 2014-06-28T21:12:35.103 回答
2

尝试以下操作:

C = type("C", (BC,), {})

类必须是模块级变量,名称与类型名称相同。

但是,像这样腌制一个动态生成的类是行不通的(请参阅@otus 的答案)。


我能想到的最佳解决方案是将参数腌制到type,然后在取消腌制时再次重新创建该类。

泡菜:

import sys, pickle

class BC(object):
    pass

args = ("NewClassName", (BC,), {})
C = type(*args)
C._pickle_args = args

pickle.dump(C._pickle_args, sys.stdout)

解压:

type_args = pickle.loads("<pickled string">)
C = type(*args)
于 2014-06-23T11:17:05.653 回答
2

该错误的一个简单解决方法是使用类名作为变量名,以便pickle可以找到它:

import sys, pickle

class BC(object):
    pass

NewClassName = type("NewClassName", (BC,), {})

pickle.dump(NewClassName, sys.stdout)

但是,这可能并不能真正满足您的要求。加载腌制类时:

pickle.loads("""c__main__
NewClassName
p0
.""")

您再次收到错误消息:

AttributeError: 'module' object has no attribute 'NewClassName'

除非你已经定义了类。


正如文档所述:

pickle 可以透明地保存和恢复类实例,但是类定义必须是可导入的,并且与存储对象时位于同一模块中。

所以你不能用它来生成新的类,只是为了确保你的对象引用正确的类。


如另一个答案type所示,有诸如酸洗参数之类的解决方法,但即使那样,如果不将类暴露在酸洗和解酸过程的全局命名空间中,您将无法酸洗这些动态类的对象(即必须参考班级)。__main__.ClassName

因此,我会重新考虑整个动态类方法。

于 2014-06-23T11:17:31.553 回答