Rye*_*yex 7 python serialization introspection
我目前正在用Python编写一个序列化模块,可以序列化用户定义的类.为了做到这一点,我需要获取对象的全名空间并将其写入文件.然后我可以使用该字符串来重新创建对象.
例如,假设我们在名为的文件中具有以下类结构 A.py
class B:
class C:
pass
Run Code Online (Sandbox Code Playgroud)
现在假设my_klass_string是字符串"A::B::C"
klasses = my_klass_string.split("::")
if globals().has_key(klasses[0]):
klass = globals()[klasses[0]]
else:
raise TypeError, "No class defined: %s} " % klasses[0]
if len(klasses) > 1:
for klass_string in klasses:
if klass.__dict__.has_key(klass_string):
klass = klass.__dict__[klass_string]
else:
raise TypeError, "No class defined: %s} " % klass_string
klass_obj = klass.__new__(klass)
Run Code Online (Sandbox Code Playgroud)
我可以创建类C的实例,即使它位于B模块中的类下A.上面的代码相当于调用eval(klass_obj = A.B.C.__new__(A.B.C))
注意:我在__new__()这里使用是因为我正在重构序列化对象,我不想初始化对象,因为我不知道类的__init__方法采用什么参数.我想在不调用init的情况下创建对象,然后再为其分配属性.
我可以A.B.C用字符串创建类的对象.我该如何走另一条路?如何从一个类的实例获取一个描述该类的完整路径的字符串,即使该类是嵌套的?
你不能以任何合理的非疯狂方式.我猜你可以找到类名和模块,然后为每个类名验证它是否存在于模块中,如果没有,则以分层方式遍历模块中存在的所有类,直到找到它为止.
但由于没有理由像这样拥有类层次结构,因此它不是问题.:-)
另外,我知道你不想在工作的这一点听到这个,但是:
跨平台序列化是一个有趣的主题,但使用这样的对象不太可能非常有用,因为目标系统必须安装完全相同的对象层次结构.因此,您必须使用两种完全相同的语言编写两个系统.这几乎是不可能的,可能不值得这么麻烦.
例如,您无法使用Pythons标准库中的任何对象,因为Ruby中不存在这些对象.最终结果是您必须创建自己的对象层次结构,最终只使用字符串和数字等基本类型.在这种情况下,您的对象刚刚成为基本原语的包含,然后您也可以使用JSON或XML序列化所有内容.
由于Python中没有这样的东西,你无法获得"给定类的实例的类的完整路径".例如,建立你的例子:
>>> class B(object):
... class C(object):
... pass
...
>>> D = B.C
>>> x = D()
>>> isinstance(x, B.C)
True
Run Code Online (Sandbox Code Playgroud)
"阶级路径" x应该是什么? D还是B.C?两者都同样有效,因此Python没有给你任何方法来告诉对方.
实际上,即使是Python的pickle模块也有麻烦腌制对象x:
>>> import pickle
>>> t = open('/tmp/x.pickle', 'w+b')
>>> pickle.dump(x, t)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.6/pickle.py", line 1362, in dump
Pickler(file, protocol).dump(obj)
...
File "/usr/lib/python2.6/pickle.py", line 748, in save_global
(obj, module, name))
pickle.PicklingError: Can't pickle <class '__main__.C'>: it's not found as __main__.C
Run Code Online (Sandbox Code Playgroud)
所以,一般来说,除了在所有类中添加一个属性(比如说_class_path)之外,我没有其他选择,你的序列化代码会查找它以将类名记录为序列化格式:
class A(object):
_class_path = 'mymodule.A'
class B(object):
_class_path = 'mymodule.A.B'
...
Run Code Online (Sandbox Code Playgroud)
您甚至可以使用某些元类魔法自动执行此操作(但如果您执行上述操作,还可以阅读相同SO帖子中的其他注释以获取警告D=B.C).
也就是说,如果你可以将序列化代码限制为(1)新式类的实例,并且(2)这些类是在模块的顶层定义的,那么你可以只复制那些pickle(save_global第730行的功能)来自Python 2.6的pickle.py中的 - 768.
这个想法是,每一个新风格的类定义的属性__name__
和__module__,这是扩大和类名(如源找到)串和模块的名称(如发现
sys.modules); 通过保存这些,您可以稍后导入模块并获取该类的实例:
__import__(module_name)
class_obj = getattr(sys.modules[module_name], class_name)
Run Code Online (Sandbox Code Playgroud)