简体   繁体   中英

Find nested sub-classes in a class in the order they're defined

Imagine a case like so:

class A:
    pass

class B:
    x = 5
    class D(A):
        pass
    class C(A):
        pass

What I want is to find all the classes in class B that are subclasses of A :

>>> for cls in dir(B):
    if issubclass(cls, A):
        print(cls)
<class '__main__.C'>
<class '__main__.D'>

And it works as intended, but the problem is: I need to get them in the order they are defiend in class B definition, so instead of printing C before D, I need to get D before C. Using dir() obviously doesn't work, since it returns alphabetically sorted list. What are my other options, if any?

EDIT: The reason I want this is to help "players" make their own heroes/champions (for a video game) as easily as possible. So instead of having to write:

class MyHero(Hero):
     def __init__(self, name='My Hero', description='My description', ...):
          super().__init__(name, description, ...)
          self.spells = [MySpell1(), MySpell2(), MySpell3()]

class MySpell1(Spell):
     def __init__(...):
          ...

They could just write:

class MyHero(Hero):
    name = 'My Hero'
    description = 'My description'
    ...

    class MySpell1(Spell):
        name = ...

    class MySpell2(Spell):
        ...

Obviously the second one looks much better than the first, and even more to a person who doesn't know much of Python.

The metaclass documentation includes a nice example of how to get a class to remember what order its members were defined in:

class OrderedClass(type):

     @classmethod
     def __prepare__(metacls, name, bases, **kwds):
        return collections.OrderedDict()

     def __new__(cls, name, bases, namespace, **kwds):
        result = type.__new__(cls, name, bases, dict(namespace))
        result.members = tuple(namespace)
        return result

class A(metaclass=OrderedClass):
    def one(self): pass
    def two(self): pass
    def three(self): pass
    def four(self): pass 


>>> A.members
('__module__', 'one', 'two', 'three', 'four')

You can adapt this to your case like this:

class A:
    pass

class B(metaclass=OrderedClass):
    x = 5
    class D(A):
        pass
    class C(A):
        pass

print(filter(lambda x: isinstance(getattr(B, x), type), b.members)))

gives:

['D', 'C']

Note that this gives you the names of the classes; if you want the classes themselves, you can do this instead:

print(list(filter(lambda x: isinstance(x, type), (getattr(B, x) for x in B.members))))

May be something like that can be helpful:

import inspect

class Spell(object):
    name = "Abstract spell"

class MyHero(object):
    name = "BATMAN"
    description = "the bat man"

    class MySpell1(Spell):
        name = "Fly"

    class MySpell2(Spell):
        name = "Sleep"


for k, v in MyHero.__dict__.iteritems():
    if inspect.isclass(v) and issubclass(v, Spell):
        print "%s cast the spell %s" % (MyHero.name, v.name)

UPDATE :

Another way to iterate by class attributes is:

for attr_name in dir(MyHero):
    attr = getattr(MyHero, attr_name)
    if inspect.isclass(attr) and issubclass(attr, Spell):
        print "%s cast the spell %s" % (MyHero.name, attr.name)

PS Python class is also object

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM