Metaclass __new__ and __init__¶
Page Maps¶
graph LR
family["Python Programming"]
program["Python Meta-Programming"]
section["Metaclass Design Class Creation"]
page["Metaclass `__new__` and `__init__`"]
capstone["Capstone evidence"]
family --> program --> section --> page
page -.applies in.-> capstone
flowchart LR
orient["Orient on the page map"] --> read["Read the main claim and examples"]
read --> inspect["Inspect the related code, proof, or capstone surface"]
inspect --> verify["Run or review the verification path"]
verify --> apply["Apply the idea back to the module and capstone"]
After resolution comes the next design question:
once the metaclass is in control, where should its work actually happen?
This core separates the two most commonly blurred hooks:
- metaclass
__new__ - metaclass
__init__
The sentence to keep¶
Use metaclass __new__ for structural decisions that must happen while the class is being
created, and use metaclass __init__ for final bookkeeping or registration on the class
object after it already exists.
That split prevents a lot of vague metaclass code.
What metaclass __new__ owns¶
__new__ receives the raw ingredients for class creation:
- the metaclass
- the class name
- the base classes
- the namespace mapping
That makes it the right place for work such as:
- namespace-driven validation
- injecting or rewriting attributes before the class object exists
- structural edits that depend on the class body itself
This is still the mutability window where the construction inputs are most direct.
What metaclass __init__ owns¶
__init__ runs after the class object has been created.
That makes it a better fit for:
- registration
- final bookkeeping
- post-creation checks that need the finished class object
It is usually the wrong place for heavy structural surgery, because the class has already been constructed.
A compact split to keep in mind¶
That is the practical rule most review discussions need.
A small example¶
class ValidatingMeta(type):
registry = []
def __new__(mcs, name, bases, namespace):
has_run_here = "run" in namespace
has_run_in_bases = any(hasattr(base, "run") for base in bases)
if not has_run_here and not has_run_in_bases:
raise TypeError(f"{name} must define run()")
namespace["from_meta"] = lambda self: "ok"
return super().__new__(mcs, name, bases, namespace)
def __init__(cls, name, bases, namespace):
super().__init__(name, bases, namespace)
ValidatingMeta.registry.append(cls)
class Task(metaclass=ValidatingMeta):
def run(self):
return "running"
This example keeps the split honest:
__new__enforces a structural rule and injects behavior__init__records the finished class in a registry
That is exactly the division of labor the module wants.
Why structural edits belong earlier¶
If a decision depends on the namespace before class creation finishes, __new__ is the
honest owner.
Examples include:
- rejecting missing required methods
- rewriting definitions based on class-body declarations
- injecting class-level helpers before the resulting class is finalized
Trying to perform that kind of work later often makes the design harder to reason about.
Why registration belongs later¶
Registration and bookkeeping usually need:
- the fully created class object
- its final bases and MRO
- a stable identity that other code can reference
That makes __init__ the more natural place for:
- appending to registries
- recording metadata
- attaching class-family bookkeeping
The rule is not absolute, but it is a very strong default.
What this split is trying to prevent¶
Without a clear split, metaclass code often turns into:
- structural mutation in
__init__ - registration side effects mixed into
__new__ - hard-to-review code that works mostly by accident
The point of this core is not stylistic purity. It is keeping class-creation behavior explainable.
A warning about overusing either hook¶
Both hooks are high-power surfaces.
That means both still need the same questions asked first:
- does this truly belong at class-creation time?
- could a class decorator still own it honestly?
- could plain explicit code be clearer?
Choosing between __new__ and __init__ is not the first question. It is the second.
Review rules for __new__ versus __init__¶
When reviewing metaclass hooks, keep these questions close:
- is this work structural or post-creation?
- does it depend on the raw namespace or the finished class object?
- is registration being kept separate from namespace mutation?
- is the metaclass doing work here that a lower-power tool could still own?
- would a reviewer be able to explain the class-creation effects in one pass?
What to practice from this page¶
Try these before moving on:
- Put one interface or namespace validation rule in
__new__and explain why it belongs there. - Add one registry append in
__init__and explain why it belongs after class creation. - Write one short review note rejecting a metaclass that mutates structure in
__init__for no clear reason.
If those feel ordinary, the next step is __prepare__, the one hook that can observe and
control assignments during class body execution itself.
Continue through Module 09¶
- Previous: Metaclass Resolution, Timing, and Conflicts
- Next:
__prepare__and Declaration-Time Enforcement - Return: Overview
- Terms: Glossary