python - how to get a list of inner classes?

2019-07-04 02:35发布

问题:

I'm writing a small Python application that contains a few nested classes, like the example below:

class SuperBar(object):
    pass

class Foo(object):
    NAME = 'this is foo'

    class Bar(SuperBar):
        MSG = 'this is how Bar handle stuff'

    class AnotherBar(SuperBar):
        MSG = 'this is how Another Bar handle stuff'

I'm using nested classes to create some sort of hierarchy and to provide a clean way to implement features for a parser.

At some point, I want to create a list of the inner classes. I'd like to have the following output:

[<class '__main__.Bar'>, <class '__main__.AnotherBar'>]

The question is: What is the recommended method to get a list of inner classes in a pythonic way?

回答1:

I managed to get a list of inner class objects with the method below:

import inspect

def inner_classes_list(cls):
    return [cls_attribute for cls_attribute in cls.__dict__.values()
            if inspect.isclass(cls_attribute)
            and issubclass(cls_attribute, SuperBar)]

It works, but I'm not sure if using __dict__ directly is a good thing to do. I'm using it because it contains the actual class instances that I need and seems to be portable across Python 2 and 3.



回答2:

First: I can't see how nested classes can be of any use for you. Once you have an instance f of Foo, do you realize that f.Bar and f.AnotherBar will be the same object for all instances? That is - you can't record any attribute specific from f on f.Bar, like f.Bar.speed - or it will collide with an attribute from another instance g.Bar.speed.

To overcome this, and actually, the only thing that makes sense, you'd need to have instances of Bar and AnotherBar attached to the instance f. These instances usually can't be declared on the class body - you have to create them on your Foo's __init__ method.

The only thing that Bar and AntherBar can do doing there is: (1) to have a lot of class and static methods, then they work as namespaces only.

Or, if a metaclass for SuperBar or themselves implement the descriptor protocol - https://docs.python.org/3/reference/datamodel.html#implementing-descriptors - but them, you'd be much better if superbar itself would implement the descriptor prootocol (by having either __get__ or __set__ methods), and attached to Foo's body you'd have instances of these classes, not the classes themselves.

That said, you came with the solution of using __dict__ to getting the inner classes: that won't work if Foo itself inherit from other classes that also have nested classes. The Superclasses of Foo are never searched. You can have a method to either look on all classes on Foo's __mro__, or simply use dir and issubclass :

class Foo:
     @classmethod
     def inner_classes_list(cls):
          results = []
          for attrname in dir(cls):
              obj = getattr(cls, attrname)
              if isinstance(obj, type) and issubclass(obj, SuperBar):
                   results.append(obj)
          return results

(If you want this to work to all classes like Foo that does not share a common base, the same code will work if it is nto declared as a class method, of course - and also, SuperBar can be a parameter to this function, if you have more than one nested-class hierarchy.)

Now you have this, we urge you to ask other questions saying what do you want to actually do - and to read about "descriptors" - and even "properties". Really: there is very little use one can think of to nested subclasses.