`__set_name__` or `__init_subclass__`: which runs first when Python creates a class?
answer
- Both hooks live inside class creation
- Attribute-level work happens before class-level
- The hook sees attributes that know their names
- Aggregate checks belong to the class-level hook
- The class name is not bound yet
basics
~20 sset_name runs first. Class creation builds the class object, notifies every attribute in the new namespace that defines set_name, and only then calls the parent's init_subclass — so the hook already sees attributes that know their own names.
solid answer
~40 sBoth hooks fire inside `type.__new__`, in a fixed order. The class object is built, then every object in the *new* class's namespace that defines `__set_name__` is called with the owner class and the attribute name it was bound to, and only after all of those have run is `__init_subclass__` called on the parent. The ordering is what makes declarative bases work: by the time your class-creation hook runs, the attributes in `vars(cls)` have already recorded their own names, so the hook can collect them, cross-validate them and build a field map without repeating the name discovery. Neither hook can look the class up by its module-level name — that binding only happens when the class statement finishes. Since Python 3.12, an exception raised inside `__set_name__` propagates as-is rather than being wrapped in a `RuntimeError`.
code
python · 13 linesclass Field:
def __set_name__(self, owner, name):
self.name = name
class Base:
def __init_subclass__(cls, /, **kwargs):
super().__init_subclass__(**kwargs)
print("class hook sees name:", cls.field.name)
class Child(Base):
field = Field()go deeper
Know that Python runs more than one hook while building a class, and that the per-attribute notification happens before the class-wide one; the exact order is something you can verify in a REPL.
Explain what each hook can see: one attribute and its owner in the first case, the whole finished class in the second, which is why aggregate work belongs in the later hook.
Use the ordering deliberately when designing a declarative base — collect and cross-validate fields in the class-level hook, keep per-attribute work minimal, and know that an error in the earlier hook masks the later checks entirely.
Judge how much class-creation machinery a codebase should carry at all: implicit hooks make bases powerful and stack traces unusual, and the ordering is a contract every extender inherits whether or not they know it exists.
## The order, stated exactly Executing a class statement runs the class body to produce a namespace, then hands name, bases and namespace to the metaclass. Inside `type.__new__` three things happen in a fixed sequence: 1. The class object itself is constructed — bases resolved, MRO computed, namespace installed. 2. Every object in the *new class's own namespace* that defines `__set_name__` is called with two arguments: the owner class and the name it was assigned to in the body. 3. `__init_subclass__` is looked up starting at the parent of the new class and called with the new class plus any keyword arguments from the class header. So `__set_name__` first, `__init_subclass__` second. Both were introduced together by PEP 487 in Python 3.6, and the ordering has been part of the contract from the start. Two scoping details ride along with that sequence. Only objects in the new class's own namespace are notified — inherited attributes are not re-notified when a subclass is created, because they are not in the subclass's namespace. And both hooks run *before* the class statement finishes, which means the class name is not yet bound in the enclosing module or function; a hook that tries `globals()[cls.__name__]` will raise `KeyError`. ## Why the order is the useful one The ordering is what makes the declarative-base pattern work at all. A base wants to collect the fields a subclass declared and do something with the collection: build a schema, check that exactly one is marked as the identity, reject two fields that claim the same storage slot. Each field is an object that needs to know its own attribute name, and `__set_name__` is how it learns that. If `__init_subclass__` ran first, every field would still be nameless and the base would have to walk `vars(cls)` and infer names itself — the very boilerplate PEP 487 removed. Because the order is the other way around, the class-creation hook sees a fully-named set of attributes and can do the *aggregate* work: ```python class Model: def __init_subclass__(cls, /, **kwargs): super().__init_subclass__(**kwargs) fields = {k: v for k, v in vars(cls).items() if isinstance(v, Field)} if not fields: raise TypeError(f"{cls.__name__} declares no fields") cls.fields = fields ``` That is the division of labour worth stating in an interview: an attribute-level hook knows about itself and its owner; the class-level hook knows about the whole class and can do cross-attribute validation. Anything that needs to see all the attributes at once belongs in the class-level hook, and it can rely on the attribute-level work already being done. ## Consequences that bite * **Do not do aggregate work in the attribute hook.** An attribute being named tells you nothing about whether its siblings have been named yet — within step 2 the order follows the namespace order, so an early attribute cannot see a later one's name. * **Error surfaces differ.** A failure in the attribute-level hook aborts class creation before the class-level hook ever runs, so a validation error there hides any error the class-level hook would have raised. If you want one clear message, do the checking in one place. * **Exception wrapping changed.** Up to Python 3.11, an exception raised by `__set_name__` was wrapped in a `RuntimeError`, which obscured the original error and its type. Since 3.12 it propagates unchanged, so `raise TypeError(...)` from an attribute hook now reaches the caller as a `TypeError`. * **The metaclass angle.** Both hooks run inside `type.__new__`, which is to say before a custom metaclass's own initialisation step gets control. That ordering is worth knowing when a codebase mixes both mechanisms, but the metaclass side of it belongs to metaclasses proper. ## How to check it rather than remember it This is the kind of detail worth demonstrating instead of reciting. Give an attribute a `__set_name__` that records its name, then print that name from the class-level hook: if it prints, the attribute hook already ran. Three lines in a REPL settle the question, and an interviewer usually values the instinct to verify a hook ordering empirically more than the memorised answer — this is trivia at the level of recall and craft at the level of knowing which hook to put work in.
- Why can't `__init_subclass__` look the new class up by its module-level name?Because the hook runs inside class creation, and the assignment of the class object to its name in the enclosing namespace only happens when the class statement completes. Inside the hook the name does not exist yet. You always have the class object as `cls`, so the lookup is unnecessary — but code that tries to reach a module-level registry keyed by that name will fail.
- Are inherited attributes re-notified when a subclass is created?No. Only objects in the new class's own namespace are notified; an attribute inherited from a base was already notified when the base was created, and it keeps the owner and name it recorded then. If an attribute genuinely needs per-subclass state, that state has to be derived in the class-level hook, which does see the whole subclass.
- Where should cross-attribute validation live, and why?In the class-level hook. An attribute-level hook is called once per attribute, in namespace order, so an early attribute cannot see whether a later one exists or what it is named. The class-level hook runs once, after all of them, with the complete class in hand — that is the only place where 'exactly one field may be the primary key' can be checked.
saying these in an interview costs you the question
- Assumes the class hook runs before attributes are named
- Thinks the ordering is unspecified or implementation-defined
- Believes the class name is already bound during the hooks
- Expects inherited attributes to be re-notified per subclass
- Puts cross-attribute validation in the per-attribute hook