python:确定一个类是否嵌套

时间:2009-03-12 15:26:54

标签: python introspection inner-classes

假设您有一个python方法,它将类型作为参数;是否可以确定给定的类型是否是嵌套类? 例如。在这个例子中:

def show_type_info(t):
    print t.__name__
    # print outer class name (if any) ...

class SomeClass:
    pass

class OuterClass:
    class InnerClass:
        pass

show_type_info(SomeClass)
show_type_info(OuterClass.InnerClass)

我希望调用show_type_info(OuterClass.InnerClass)来显示InnerClass是在OuterClass中定义的。

6 个答案:

答案 0 :(得分:13)

AFAIK,给定一个类而没有其他信息,你无法判断它是否是一个嵌套类。但是,see here表示您可以使用装饰器来确定这一点。

问题是嵌套类只是一个普通类,它是外部类的属性。您可能期望工作的其他解决方案可能不会 - 例如,inspect.getmro仅为您提供基类,而不是外部类。

此外,很少需要嵌套类。我会强烈重新考虑在每个特殊情况下这是一个很好的方法,你会觉得很想使用它。

答案 1 :(得分:5)

内部类在Python中没有提供特殊的特性。它只是类对象的属性,与整数或字符串属性没有区别。您的OuterClass / InnerClass示例可以完全重写为:

class OuterClass(): pass
class InnerClass(): pass
OuterClass.InnerClass= InnerClass

InnerClass无法知道它是否在另一个类中声明,因为它只是一个普通的变量绑定。使绑定方法了解其拥有者“自我”的魔力在这里不适用。

John发布的链接中的内部类装饰魔术是一种有趣的方法,但我不会按原样使用它。它不会缓存为每个外部对象创建的类,因此每次调用outerinstance.InnerClass时都会得到一个新的InnerClass:

>>> o= OuterClass()
>>> i= o.InnerClass()
>>> isinstance(i, o.InnerClass)
False # huh?
>>> o.InnerClass is o.InnerClass
False # oh, whoops...

此外,它尝试复制使用getattr / setattr在内部类上使外部类变量可用的Java行为的方式非常狡猾,并且实际上是不必要的(因为更多的Pythonic方式是明确地调用i .__ outer __。attr )。

答案 2 :(得分:4)

实际上,嵌套类与任何其他类没有什么不同 - 它恰好被定义在除顶级命名空间之外的其他地方(在另一个类中)。如果我们将描述从“嵌套”修改为“非顶级”,那么您可能已经足够接近您需要的内容。

例如:

import inspect

def not_toplevel(cls):
    m = inspect.getmodule(cls)
    return not (getattr(m, cls.__name__, []) is cls)

这适用于常见情况,但在定义后重命名或以其他方式操作的情况下,它可能无法执行您想要的操作。例如:

class C:             # not_toplevel(C) = False
    class B: pass    # not_toplevel(C.B) = True

B=C.B                # not_toplevel(B) = True

D=C                  # D is defined at the top, but...
del C                # not_toplevel(D) = True

def getclass():      # not_toplevel(getclass()) = True
    class C: pass

答案 3 :(得分:1)

谢谢大家的回答 我发现这种可能的解决方案使用元类;我已经做了更多的顽固而不是真正的需要,并且它以一种不适用于python 3的方式完成。
我想分享这个解决方案,所以我在这里发布。

#!/usr/bin/env python
class ScopeInfo(type): # stores scope information
    __outers={} # outer classes
    def __init__(cls, name, bases, dict):
        super(ScopeInfo, cls).__init__(name, bases, dict)
        ScopeInfo.__outers[cls] = None
        for v in dict.values(): # iterate objects in the class's dictionary
            for t in ScopeInfo.__outers:
                if (v == t): # is the object an already registered type?
                    ScopeInfo.__outers[t] = cls
                    break;
    def FullyQualifiedName(cls):
        c = ScopeInfo.__outers[cls]
        if c is None:
            return "%s::%s" % (cls.__module__,cls.__name__)
        else:
            return "%s.%s" % (c.FullyQualifiedName(),cls.__name__)

__metaclass__ = ScopeInfo

class Outer:
    class Inner:
        class EvenMoreInner:
            pass

print Outer.FullyQualifiedName()
print Outer.Inner.FullyQualifiedName()
print Outer.Inner.EvenMoreInner.FullyQualifiedName()
X = Outer.Inner
del Outer.Inner
print X.FullyQualifiedName()

答案 4 :(得分:0)

如果您没有自己设置,我不相信有任何方法可以确定该类是否嵌套。无论如何,Python类不能用作命名空间(或者至少不容易),我想说最好的办法就是使用不同的文件。

答案 5 :(得分:0)

从Python 3.3开始,有一个新属性__qualname__,它不仅提供类名,还提供外部类的名称:

在您的示例中,这将导致:

assert SomeClass.__qualname__ == 'SomeClass'
assert OuterClass.InnerClass.__qualname__ == 'OuterClass.InnerClass'

如果某个类的__qualname__不包含'。'这是一个外层阶层!