Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: How to register all child classes with the father class upon creation

I have python class trees, each made up of an abstract base class and many deriving concrete classes. I want all concrete classes to be accessible through a base-class method, and I do not want to specify anything during child-class creation.

This is what my imagined 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'd prefer to write the "magic" once as a sort of design pattern. I want to be able to apply it to different class trees in different scenarios (i.e. add a similar tree with "BaseClassB" and its concrete classes).

Thanks Internet!

like image 404
Yonatan Avatar asked Dec 12 '22 11:12

Yonatan


2 Answers

you can use meta classes for that:

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'm personnaly very wary of this kind of magic. Don't put too much of this in your code.

like image 94
gurney alex Avatar answered May 02 '23 18:05

gurney alex


Here is a simple solution using modern python's (3.6+) __init__subclass__ defined in PEP 487. It allows you to avoid using a meta-class.

class BaseClassA(object):
    _subclasses = [] 

    @classmethod
    def get_concrete_classes(cls):
        return list(cls._subclasses)

    def __init_subclass__(cls):
        BaseClassA._subclasses.append(cls)


class ConcreteClassA1(BaseClassA):
    pass  # no magic-related code here


class ConcreteClassA2(BaseClassA):
    pass  # no magic-related code here


print(BaseClassA.get_concrete_classes())
like image 38
derchambers Avatar answered May 02 '23 19:05

derchambers