Python: determine if a class is nested

Suppose you have a python method that receives a type as a parameter; Is it possible to determine if a given type is a nested class?
For example. 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 also show that InnerClass is defined inside OuterClass.

+5
source share
5 answers

AFAIK, given the class and no other information, you cannot determine if it is a nested class. However, see here for how you can use a decorator to define this.

The problem is that the nested class is just a normal class, which is an attribute of its outer class. Other solutions that you might expect to work will probably not - inspect.getmro , for example, only provides base classes, not external classes.

In addition, nested classes are rarely needed. I would strongly reconsider whether this is good on a case-by-case basis when you feel like using it.

+12
source

The inner class does not offer special features in Python. This is only a property of a class object, other than integer or string. Your OuterClass / InnerClass example can be rewritten in the same way:

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

InnerClass cannot know if it was declared inside another class, because it is just a variable binding. The magic that makes related methods know about its owner is not applied here.

The way the decorator of the inner class in the link posted by John is an interesting approach, but I would not use it as it is. It does not cache the classes that it creates for each external object, so every time you call externalinstance, you get a new InnerClass. InnerClass:

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

Also the way he tries to replicate Java behavior by making class external variables available in the inner class using getattr / setattr is very dodgy and unnecessary (since the more Pythonic path would have to call i .__ external __. Attr explicitly) .

+5
source

Indeed, a nested class is no different from any other class - it is simply defined somewhere else than the top-level namespace (instead, inside another class). If we change the description from "nested" to "non-top level", then you can get closer 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 ordinary cases, but may not do what you want in situations where classes are renamed or otherwise processed after definition. For example:

 class C: # not_toplevel(C) = False class B: pass # not_toplevel(CB) = True B=CB # 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
source

Thank you all for your answers.
I found this possible solution using metaclasses; I did this more for stubbornness than a real need, and it was made so that it would not be applicable to python 3.
I want to share this solution anyway, so I post 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 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() 
+1
source

If you do not install it yourself, I do not believe that there is any way to determine if the class is nested. No matter how the Python class can be used as a namespace (or at least not easily), I would say that it is best to just use different files.

0
source

All Articles