Skip to content
Hello Python
1/7

Metaclasses & Object Construction

Topic 1 of 7, with 3 concept checks. __new__ vs __init__, and how classes are actually built

Follow class creation from namespace to instance

Class construction

Trace how Python prepares a class namespace, invokes a metaclass, creates the class object, and later constructs instances before deciding whether customization is justified.

Core lesson 01

__new__(cls, ...) is a static method that creates and returns a new instance, called before the instance exists. __init__(self, ...) then initializes that already-created instance and returns nothing.

Object creation is actually two steps in Python. __new__ is responsible for allocating and returning a new instance of the class (implicitly a staticmethod, taking cls first). Python then calls __init__ on that returned instance to set up its initial state. Overriding __init__ is common; overriding __new__ is rare, mainly used for immutable-type subclassing or enforcing patterns like singletons where you need to control which object gets returned before initialization even happens.

Python example
class Singleton:
    _instance = None
    def __new__(cls):
        if cls._instance is None:
            print("creating the one instance")
            cls._instance = super().__new__(cls)
        return cls._instance
    def __init__(self):
        print("init runs every time __init__ is called")

a = Singleton()
b = Singleton()
print(a is b)   # True -- same object
# note: __init__ still runs twice, once per Singleton() call

What to remember

What's the difference between __new__ and __init__?

Common footguns

  • Assuming overriding __new__ alone prevents __init__ from re-running on repeat calls — it doesn't; __init__ always runs after __new__ returns, even if __new__ returned an existing object.

Core lesson 02

A metaclass is the class of a class — it controls how classes themselves are created. type is the default metaclass; class Foo: ... is really syntactic sugar for calling type('Foo', bases, namespace).

Just as instantiating a class produces an object, 'instantiating' a metaclass produces a class. By default, every class's metaclass is type — when Python executes a class statement, it's effectively calling type(name, bases, namespace) to build the class object. A custom metaclass (a subclass of type) can hook into that process — validating class definitions, auto-registering subclasses, injecting methods — by overriding __new__ or __init__ on the metaclass itself.

Python example
class Meta(type):
    def __new__(mcls, name, bases, namespace):
        print(f"creating class {name}")
        return super().__new__(mcls, name, bases, namespace)

class MyClass(metaclass=Meta):
    pass
# prints: creating class MyClass -- this runs at class-DEFINITION time, not instantiation

What to remember

What is a metaclass, and what does 'type is the metaclass of all classes' mean?

Common footguns

  • Reaching for a metaclass to solve a problem a simpler tool (a decorator, __init_subclass__, or a plain base class) would solve more clearly — metaclasses hurt readability if overused.

Core lesson 03

__init_subclass__(cls) is a classmethod on a base class that Python calls automatically whenever a subclass is defined — covers the most common metaclass use case without the complexity of a full metaclass.

Before Python 3.6, reacting to 'a new subclass was just defined' required a metaclass. __init_subclass__ provides the same hook as a plain classmethod on the class you want to watch — much easier to read and reason about, and composes better with normal inheritance.

Python example
class Plugin:
    registry = []
    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        Plugin.registry.append(cls)

class PluginA(Plugin): pass
class PluginB(Plugin): pass

print(Plugin.registry)   # [<class PluginA>, <class PluginB>] -- auto-registered

What to remember

What's a simpler alternative to a metaclass for reacting to subclass creation?

Common footguns

  • Forgetting super().__init_subclass__(**kwargs) — breaks cooperative behavior if a chain of subclasses also uses this hook.

Python lab

Browser Python lab

Runtime · idle

Python loads on your first run. Your code stays in this browser.

Best practices

  • Reach for __init_subclass__ before a full metaclass for subclass registration/validation — simpler and clearer.
  • Override __new__ only for immutable-type subclassing or true singleton-style construction control.
  • Keep metaclasses (if used at all) narrowly scoped and well documented — they affect anyone who ever subclasses the class.
  • Prefer composition, decorators, or class decorators over metaclasses for most 'inject behavior into classes' needs.

Apply the concept in Interview practice

Design Search Autocomplete SystemhardLeetCode #642 · O(p) per keystroke (p = matches)

Build a trie of past sentences with frequency counts; on each keystroke walk to the current trie node and return the top 3 matches by frequency then lexicographic order.

Open problem
Logger Rate LimitereasyLeetCode #359 · O(1) per message

Keep a dict of message -> last-printed timestamp; allow a message through only if it hasn't been seen in the last 10 seconds — a class-level registry pattern similar to what metaclasses often manage.

Open problem

Concept checks

Q01

What's the difference between __new__ and __init__?

Q02

What is a metaclass, and what does 'type is the metaclass of all classes' mean?

Q03

What's a simpler alternative to a metaclass for reacting to subclass creation?