在python中获取类的路径或名称空间,即使它是嵌套的

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用字符串创建类的对象.我该如何走另一条路?如何从一个类的实例获取一个描述该类的完整路径的字符串,即使该类是嵌套的?

Len*_*bro 6

你不能以任何合理的非疯狂方式.我猜你可以找到类名和模块,然后为每个类名验证它是否存在于模块中,如果没有,则以分层方式遍历模块中存在的所有类,直到找到它为止.

但由于没有理由像这样拥有类层次结构,因此它不是问题.:-)

另外,我知道你不想在工作的这一点听到这个,但是:

跨平台序列化是一个有趣的主题,但使用这样的对象不太可能非常有用,因为目标系统必须安装完全相同的对象层次结构.因此,您必须使用两种完全相同的语言编写两个系统.这几乎是不可能的,可能不值得这么麻烦.

例如,您无法使用Pythons标准库中的任何对象,因为Ruby中不存在这些对象.最终结果是您必须创建自己的对象层次结构,最终只使用字符串和数字等基本类型.在这种情况下,您的对象刚刚成为基本原语的包含,然后您也可以使用JSON或XML序列化所有内容.

  • 这并没有回答我为什么要序列化对象的问题.您可以一方面将数据导出为某种理智的格式,如JSON或XML,然后在另一侧读取.这更简单,更有可能实际工作.在这种情况下,如果您拥有的任何对象在另一侧没有表示,则它将失败.如果你在Python端有一个datetime对象,你*必须*在Ruby端有一个名为"datetime"的模块,其中一个名为"datetime"的类具有与Pythons datetime.datetime完全相同的属性,否则它将无效.你有这个吗? (2认同)

Ric*_*rri 6

由于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)