python: determine if a class is nested

折月煮酒 提交于 2019-12-07 03:41:43

问题


Suppose you have a python method that gets a type as parameter; is it possible to determine if the given type is a nested class?
E.g. in this example:

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)

I would like the call to show_type_info(OuterClass.InnerClass) to show also that InnerClass is defined inside OuterClass.


回答1:


AFAIK, given a class and no other information, you can't tell whether or not it's a nested class. However, see here for how you might use a decorator to determine this.

The problem is that a nested class is simply a normal class that's an attribute of its outer class. Other solutions that you might expect to work probably won't -- inspect.getmro, for example, only gives you base classes, not outer classes.

Also, nested classes are rarely needed. I would strongly reconsider whether that's a good approach in each particular case where you feel tempted to use one.




回答2:


An inner class offers no particular special features in Python. It's only a property of the class object, no different from an integer or string property. Your OuterClass/InnerClass example can be rewritten exactly as:

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

InnerClass can't know whether it was declared inside another class, because that's just a plain variable binding. The magic that makes bound methods know about their owner ‘self’ doesn't apply here.

The innerclass decorator magic in the link John posted is an interesting approach but I would not use it as-is. It doesn't cache the classes it creates for each outer object, so you get a new InnerClass every time you call outerinstance.InnerClass:

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

Also the way it tries to replicate the Java behaviour of making outer class variables available on the inner class with getattr/setattr is very dodgy, and unnecessary really (since the more Pythonic way would be to call i.__outer__.attr explicitly).




回答3:


Really a nested class is no different from any other class - it just happens to be defined somewhere else than the top-level namespace (inside another class instead). If we modify the description from "nested" to "non-top-level", then you may be able to come close enough to what you need.

eg:

import inspect

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

This will work for common cases, but it may not do what you want in situations where classes are renamed or otherwise manipulated after definition. For example:

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



回答4:


Thank you all for your answers.
I've found this possible solution using metaclasses; I've done it more for obstination than real need, and it's done in a way that will not be applicable to python 3.
I want to share this solution anyway, so I'm posting it here.

#!/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()



回答5:


If you do not set it yourself, I do not believe that there is any way to determine if the class is nested. As anyway a Python class cannot be used as a namespace (or at least not easily), I would say that the best thing to do is simply use different files.




回答6:


Beginning with Python 3.3 there is a new attribute __qualname__, which provides not only the class name, but also the names of the outer classes:

In your sample this would result in:

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

If __qualname__ of a class does not contains a '.' it is an outer class!



来源:https://stackoverflow.com/questions/639162/python-determine-if-a-class-is-nested

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!