6

假设您有一个获取类型作为参数的 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 中定义的。

4

6 回答 6

13

AFAIK,给定一个类而没有其他信息,您无法判断它是否是嵌套类。但是,请参阅此处了解如何使用装饰器来确定这一点。

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

此外,很少需要嵌套类。我会强烈重新考虑这是否是一种在您想使用它的特定情况下的好方法。

于 2009-03-12T15:34:51.020 回答
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...

此外,它试图复制 Java 行为,即使用 getattr/setattr 在内部类上提供外部类变量是非常狡猾的,而且实际上是不必要的(因为更 Pythonic 的方式是显式调用 i.__outer__.attr)。

于 2009-03-12T16:23:04.447 回答
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
于 2009-03-12T16:46:50.987 回答
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()
于 2009-03-12T17:14:20.250 回答
0

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

于 2009-03-12T16:37:15.567 回答
0

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

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

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

如果__qualname__一个类不包含'.' 这是一个外部类!

于 2018-04-22T18:53:13.857 回答