Class Escape Hatches
Some classes can’t be expressed as @dataclass(slots=True) because their base class needs configuration to run before fields are assigned. class! is the escape hatch.
When you need class!
If subclassing any of these:
torch.nn.Moduleenum.Enumtyping.NamedTupleunittest.TestCase- Django models (
django.db.models.Model) - SQLAlchemy declarative bases (
DeclarativeBase) - Custom framework classes with non-trivial
__init__
How class! differs from class
- No
@dataclassdecorator. __init__is auto-synthesised when the body declares nodef __init__and at least one base is present. The synthesised constructor callssuper().__init__()first, then assigns annotated fields in source order. Required fields come before defaulted ones in the signature.- A
class!chain composes its constructors. When the first base is another in-moduleclass!(without its own__init__), the subclass takes the base’s parameters — defaults included — ahead of its own fields and forwards them by keyword:class! Grand(Child): c: stroverclass! Child(Base): a: int; b: int = 2isGrand(a, c, b=2)withsuper().__init__(a=a, b=b). A framework base such asnn.Modulestill runs its__init__before any field is assigned. - Hand-written
__init__is preserved verbatim — use when the base needs configuration arguments that aren’t 1:1 with declared fields.
torch.nn.Module
import torch.nn as nn
class! MyModel(nn.Module): layer1: nn.Linear layer2: nn.Linear dropout: float
def forward(self, x): return self.layer2(self.layer1(x))import torch.nn as nn
class MyModel(nn.Module): layer1: nn.Linear layer2: nn.Linear dropout: float
def __init__(self, layer1: nn.Linear, layer2: nn.Linear, dropout: float) -> None: super().__init__() self.layer1 = layer1 self.layer2 = layer2 self.dropout = dropout
def forward(self, x): return self.layer2(self.layer1(x))super().__init__() runs first — nn.Module’s setup must complete before fields can be assigned.
enum.Enum
from enum import Enum
class! Status(Enum): ACTIVE = "active" PAUSED = "paused" DELETED = "deleted"from enum import Enum
class Status(Enum): ACTIVE = "active" PAUSED = "paused" DELETED = "deleted"Enums use class-level name = value assignments. class! preserves them verbatim and skips __init__ synthesis (Enum’s metaclass owns construction).
unittest.TestCase
import unittest
class! TestUsers(unittest.TestCase): def test_creation(self) -> None: let u = User(id=1, name="a") self.assertEqual(u.id, 1)import unittest
class TestUsers(unittest.TestCase): def test_creation(self) -> None: u = User(id=1, name="a") self.assertEqual(u.id, 1)No annotated fields → no synthesised __init__. unittest’s machinery owns the test instance lifecycle.
Django models
from django.db import models
class! UserModel(models.Model): name = models.CharField(max_length=100) email = models.EmailField()
class Meta: db_table = "users"from django.db import models
class UserModel(models.Model): name = models.CharField(max_length=100) email = models.EmailField()
class Meta: db_table = "users"Django’s Model base has its own metaclass; assignments like name = models.CharField(...) are class-level declarations, not field annotations. class! preserves them verbatim.
SQLAlchemy declarative
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
class Base(DeclarativeBase): pass
class! User(Base): __tablename__ = "users" id: Mapped[int] = mapped_column(primary_key=True) name: Mapped[str] = mapped_column()from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
class Base(DeclarativeBase): pass
class User(Base): __tablename__ = "users" id: Mapped[int] = mapped_column(primary_key=True) name: Mapped[str] = mapped_column()When not to use class!
For your own value types — use plain class or model. class! exists for the cases where the framework owns construction.
Where next
- Classes and Models (tour) — when to reach for which.
class,model,class!reference — declaration forms.