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

时间:2010-12-17 06:23:17

标签: python serialization introspection

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

4 个答案:

答案 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的“课程路径”应该是什么? DB.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编写一个序列化模块,可以序列化用户定义的类。

<强>不即可。标准库已包含一个。实际上,根据您的计算方式,它至少包括两个(pickleshelve)。

答案 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'>

算法:

  1. 使用与C
  2. 相同的__module__搜索类词典
  3. 上课,使用'C'属性
  4. 如果此类是嵌套的。反复地
  5. 解决方案2

    使用源文件。使用inspect来获取类的行并向上扫描嵌套它的新类。

    注意:我知道在python 2中没有干净的方法,但python 3提供了一个。