动态类定义的序列化
我正在尝试把一个动态生成的类保存成一个工厂,用来创建另一种类。就像下面这样:
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,因为我只需要它作为新类的工厂。
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
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)
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
必须指向这个类)。
因此,我建议你重新考虑整个动态类的方法。