我目前正在用Python编写一个可以序列化用户定义类的序列化模块。为了做到这一点,我需要获取对象的全名空间并将其写入文件。然后我可以使用该字符串重新创建对象。
例如假设我们在名为A.py
class B:
class C:
pass
现在假设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)
我可以创建C类的实例,即使它位于模块B
中的类A
下。
上面的代码相当于调用eval(klass_obj = A.B.C.__new__(A.B.C))
请注意:
我在这里使用__new__()
因为我正在重构一个序列化对象而我不想初始化该对象,因为我不知道该类的__init__
方法采用什么参数。我想在不调用 init 的情况下创建对象,然后再为其分配属性。
我可以用字符串创建类A.B.C
的对象。我该如何走另一条路?如何从一个类的实例中获取一个描述该类的完整路径的字符串,即使该类是嵌套的?
答案 0 :(得分:6)
你不能以任何合理的非疯狂方式。我猜你可以找到类名和模块,然后为每个类名验证它是否存在于模块中,如果没有,则以分层方式遍历模块中存在的所有类,直到找到它为止。 / p>
但是由于没有理由像这样拥有类层次结构,所以它不是问题。 : - )
另外,我知道你现在不想在工作中听到这个,但是:
跨平台序列化是一个有趣的主题,但使用这样的对象不太可能非常有用,因为目标系统必须安装完全相同的对象层次结构。因此,您必须使用两种不同语言编写的两个系统完全等效。这几乎是不可能的,可能不值得这么麻烦。
例如,您无法使用Pythons标准库中的任何对象,因为Ruby中不存在这些对象。最终结果是您必须创建自己的对象层次结构,最终只使用字符串和数字等基本类型。在这种情况下,您的对象刚刚成为基本图元的包含, 然后您也可以使用JSON或XML序列化所有内容。
答案 1 :(得分:6)
你不能得到“给定一个实例的类的完整路径 class“,因为在Python中没有这样的东西。对于 例如,建立你的例子:
>>> class B(object):
... class C(object):
... pass
...
>>> D = B.C
>>> x = D()
>>> isinstance(x, B.C)
True
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
因此,一般来说,除了添加属性之外,我没有其他选择
对你的所有课程(比如_class_path
),你的序列化代码会查找它
将类名记录为序列化格式:
class A(object):
_class_path = 'mymodule.A'
class B(object):
_class_path = 'mymodule.A.B'
...
您甚至可以使用some metaclass magic自动执行此操作(但也请阅读the same SO post中的其他注释,了解上述D=B.C
可能适用的警告。
也就是说,如果可以将序列化代码限制为(1)个实例
新风格的类,以及(2)这些类是在
模块的顶级,然后您可以复制pickle
的内容
(函数save_global
位于Python的pickle.py中的730--768行
2.6)。
这个想法是每个新式的类定义属性__name__
和__module__
,它们是扩展为类名的字符串(如
在源代码中找到)和模块名称(如
sys.modules
);通过保存这些,您可以稍后导入模块和
获取类的实例:
__import__(module_name)
class_obj = getattr(sys.modules[module_name], class_name)
答案 2 :(得分:1)
我目前正在用Python编写一个序列化模块,可以序列化用户定义的类。
<强>不即可。标准库已包含一个。实际上,根据您的计算方式,它至少包括两个(pickle
和shelve
)。
答案 3 :(得分:0)
有两种方法。
解决方案1
第一个通过垃圾收集器。
B -> __dict__ -> C
这是代码:
>>> class B(object):
class C(object):
pass
>>> gc.get_referrers(B.C) # last element in the list
[<attribute '__dict__' of 'C' objects>, <attribute '__weakref__' of 'C' objects>, (<class '__main__.C'>, <type 'object'>), {'__dict__': <attribute '__dict__' of 'B' objects>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'B' objects>, 'C': <class '__main__.C'>, '__doc__': None}]
>>> gc.get_referrers(gc.get_referrers(B.C)[-1]) # first element in this list
[<class '__main__.B'>, [<attribute '__dict__' of 'C' objects>, <attribute '__weakref__' of 'C' objects>, (<class '__main__.C'>, <type 'object'>), {'__dict__': <attribute '__dict__' of 'B' objects>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'B' objects>, 'C': <class '__main__.C'>, '__doc__': None}]]
>>> gc.get_referrers(gc.get_referrers(B.C)[-1])[0]
<class '__main__.B'>
算法:
__module__
搜索类词典
解决方案2
使用源文件。使用inspect来获取类的行并向上扫描嵌套它的新类。
注意:我知道在python 2中没有干净的方法,但python 3提供了一个。