Python: how to register all child classes with father class when creating

I have python class trees, each of which consists of an abstract base class and many, invoking specific classes. I want all concrete classes to be accessible through a base class method, and I don't want to specify anything while creating a child class.

Here's what my imaginary solution looks like:

class BaseClassA(object): # <some magic code around here> @classmethod def getConcreteClasses(cls): # <some magic related code here> class ConcreteClassA1(BaseClassA): # no magic-related code here class ConcreteClassA2(BaseClassA): # no magic-related code here 

As much as possible, I would rather write β€œmagic” once as a kind of design pattern. I want to be able to apply it to different class trees in different scenarios (for example, add a similar tree with "BaseClassB" and its specific classes).

Thank you internet!

+4
source share
3 answers

you can use meta classes for this:

 class AutoRegister(type): def __new__(mcs, name, bases, classdict): new_cls = type.__new__(mcs, name, bases, classdict) #print mcs, name, bases, classdict for b in bases: if hasattr(b, 'register_subclass'): b.register_subclass(new_cls) return new_cls class AbstractClassA(object): __metaclass__ = AutoRegister _subclasses = [] @classmethod def register_subclass(klass, cls): klass._subclasses.append(cls) @classmethod def get_concrete_classes(klass): return klass._subclasses class ConcreteClassA1(AbstractClassA): pass class ConcreteClassA2(AbstractClassA): pass class ConcreteClassA3(ConcreteClassA2): pass print AbstractClassA.get_concrete_classes() 

I am very afraid of such magic. Do not put too much of this in your code.

+6
source

You should know that part of the answer you are looking for is inline. New-style classes automatically maintain a weak reference to all their child classes, which can be accessed using the __subclasses__ method:

 @classmethod def getConcreteClasses(cls): return cls.__subclasses__() 

This will not return subclasses. If you need it, you can create a recursive generator to get everything:

 @classmethod def getConcreteClasses(cls): for c in cls.__subclasses__(): yield c for c2 in c.getConcreteClasses(): yield c2 
+2
source

Another way to do this is with a decorator if your subclasses either do not define __init__ or call their parent __init__ :

 def lister(cls): cls.classes = list() cls._init = cls.__init__ def init(self, *args, **kwargs): cls = self.__class__ if cls not in cls.classes: cls.classes.append(cls) cls._init(self, *args, **kwargs) cls.__init__ = init @classmethod def getclasses(cls): return cls.classes cls.getclasses = getclasses return cls @lister class A(object): pass class B(A): pass class C(A): def __init__(self): super(C, self).__init__() b = B() c = C() c2 = C() print 'Classes:', c.getclasses() 

Will work regardless of whether the base class defines __init__ .

+1
source

All Articles