· Hakan Çelik · Python / Metaclass · 2 dk okuma

Register Classes in Python

Three ways to automatically register subclasses: metaclass __new__, __init_subclass__, and class decorator. Which one is more appropriate for which situation?

Register Classes in Python

Automatically adding subclasses that derive from a base class to a list is a common need — it is used in plugin systems, command registries, or factory patterns. Below, three different approaches that produce the same result are compared:

class Meta(type):
    _names = []

    def __new__(mcs, name, bases, namespace, **kwargs):
        obj = super().__new__(mcs, name, bases, namespace)
        if not [b for b in bases if isinstance(b, mcs)]:  # mcs is a Base
            return obj

        mcs._names.append(name)
        return obj

class Base(metaclass=Meta):
    pass

class Example(Base):
    pass

class Example2(Base):
    pass

assert Example._names == ["Example", "Example2"]
assert Example2._names == ["Example", "Example2"]

# -------

class Base:
    _names = []

    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        cls._names.append(cls.__name__)

class Example(Base):
    pass

class Example2(Base):
    pass

assert Example._names == ["Example", "Example2"]
assert Example2._names == ["Example", "Example2"]

# -------

def decorator(cls):
    if hasattr(cls, "_names"):
        cls._names.append(cls.__name__)
    else:
        cls._names = []

    return cls

@decorator
class Base:
    pass

@decorator
class Example(Base):
    pass

@decorator
class Example2(Base):
    pass

assert Example._names == ["Example", "Example2"]
assert Example2._names == ["Example", "Example2"]

Approach 1: Metaclass __new__

Meta.__new__ runs for every class definition — for both Base and its subclasses. The condition not [b for b in bases if isinstance(b, mcs)] means “if none of the inherited bases is an instance of Meta, this is the Base class — don’t register it”. This way only real subclasses are added to the list.

Approach 2: __init_subclass__

Added in Python 3.6. When a class is derived from another class, that class’s __init_subclass__ method is called. No metaclass needs to be written and the code is much cleaner. For simple subclass registration needs, __init_subclass__ is the recommended choice.

Approach 3: Decorator

The most explicit approach — there is no hidden mechanism. @decorator must be applied manually to each class; this is both its strength and its weakness. It is easy to forget with many classes, but what is registered is always clearly visible.

Which Approach Should You Choose?

  • If automatic behavior and complex control are needed → Metaclass
  • For simple subclass tracking → __init_subclass__
  • For selective and explicit registration → Decorator
Back to Blog

Related Posts

View All Posts »
Understanding Python Classes

Understanding Python Classes

Python · 2 dk

In Python, everything is an object and every object has a type — including primitives, functions, and classes themselves. type() and __class__ reveal this relationship.

Run Methods Order In Python

Run Methods Order In Python

Python · 2 dk

Which method runs when in Python metaclasses? The execution order of __prepare__, __new__, __init__, and __call__ during class definition and instance creation.