Skip to main content

Lesson 2: Practical Metaclass Patterns – Validation, Registration, and Framework Design

Why This Matters

In Lesson 1, you learned how metaclasses work. In this lesson, you'll learn why they exist—by solving real problems that would be impossible with regular code.

Consider this scenario: You're building a plugin system. Developers write plugin classes, and you need every plugin automatically registered in a central registry. Without a metaclass, you'd require boilerplate:

class Plugin1:
pass

REGISTRY['plugin1'] = Plugin1 # Manual registration—error-prone

With a registration metaclass, it happens automatically:

class Plugin1(metaclass=PluginRegistry):
pass

# Plugin1 automatically in REGISTRY — no manual work

That's the power of metaclasses: automating class creation logic at the framework level. This lesson shows you five practical patterns used in real frameworks (Django, SQLAlchemy, etc.), and teaches you when NOT to use a metaclass (spoiler: __init_subclass__ is often simpler).

By the end, you'll be able to read framework source code and understand why they chose metaclasses over simpler approaches.

Pattern 1: Attribute Validation Metaclass

Building on Lesson 1's validation example, let's deepen this pattern for production use.

The Problem: You have a framework where all plugin classes must define certain attributes (e.g., name, version, author). You want to enforce this at class definition time, not runtime.

Example: Plugin Framework with Validation

# Specification Reference: Enforce required attributes on all subclasses
# Prompt: Create a metaclass that validates required attributes and provides helpful error messages

class PluginMeta(type):
"""Metaclass that enforces required attributes on all plugin classes."""

required_attributes: list[str] = ['name', 'version', 'author']

def __new__(mcs, name: str, bases: tuple, namespace: dict) -> type:
"""Validate required attributes before creating the plugin class."""
# Skip validation for abstract base classes
if 'AbstractPlugin' in name or len(bases) == 0:
return super().__new__(mcs, name, bases, namespace)

# Check for required attributes
missing: list[str] = [
attr for attr in mcs.required_attributes
if attr not in namespace
]

if missing:
raise TypeError(
f"Plugin class '{name}' missing required attributes: {missing}. "
f"All plugins must define: {', '.join(mcs.required_attributes)}"
)

# Validate attribute types
if not isinstance(namespace.get('version'), str):
raise TypeError(f"Plugin '{name}': 'version' must be a string, not {type(namespace['version']).__name__}")

# Create the class
cls = super().__new__(mcs, name, bases, namespace)

# Store metadata about the plugin
cls._validated_at_creation = True
return cls


# Example: Valid plugin
class DataProcessor(metaclass=PluginMeta):
"""A plugin that processes data."""
name = "data_processor"
version = "1.0.0"
author = "Alice"

def process(self, data: dict) -> dict:
"""Process data and return result."""
return data


# Example: Invalid plugin (missing 'author')
try:
class BrokenPlugin(metaclass=PluginMeta):
name = "broken"
version = "1.0.0"
# Missing 'author' — will raise TypeError
except TypeError as e:
print(f"Validation error: {e}")
# Output: Validation error: Plugin class 'BrokenPlugin' missing required attributes: ['author']...

Validation Steps:

  1. ✅ Valid plugin created successfully
  2. ✅ Invalid plugin raises TypeError with clear error message
  3. ✅ Error occurs at class definition time (not instantiation)
  4. ✅ Plugin instance works normally: processor = DataProcessor() and processor.process(...)

Key Insight: Validation metaclasses catch errors early, at class definition time, before code even tries to use the plugin. This is much better than runtime errors deep in your application.

Pattern 2: Class Registration Metaclass

The Problem: You have a plugin system. Every plugin class should automatically register itself in a central registry. Developers shouldn't have to manually register.

Example: Auto-Registering Plugin System

# Specification Reference: Auto-register subclasses in registry
# Prompt: Create a metaclass that automatically registers every class in a global registry dict

class RegistryMeta(type):
"""Metaclass that auto-registers all classes in a global registry."""

# Class-level registry (shared across all classes using this metaclass)
registry: dict[str, type] = {}

def __new__(mcs, name: str, bases: tuple, namespace: dict) -> type:
"""Create class and register it."""
cls = super().__new__(mcs, name, bases, namespace)

# Don't register the base class itself (too abstract)
if name != 'Plugin':
# Register using lowercase class name as key
registry_name = name.lower()
mcs.registry[registry_name] = cls
print(f"[Registry] Registered plugin: {registry_name}")

return cls

@classmethod
def get_registry(mcs) -> dict[str, type]:
"""Get the full registry of all registered classes."""
return mcs.registry.copy()


# Base class using the registry metaclass
class Plugin(metaclass=RegistryMeta):
"""Base class for all plugins."""

def execute(self) -> str:
raise NotImplementedError("Subclasses must implement execute()")


# Define plugins — they auto-register
class EmailPlugin(Plugin):
"""Send email notifications."""
def execute(self) -> str:
return "Sending email..."

class SlackPlugin(Plugin):
"""Send Slack messages."""
def execute(self) -> str:
return "Sending to Slack..."

class DatabasePlugin(Plugin):
"""Store data in database."""
def execute(self) -> str:
return "Storing in database..."

# Check the registry
print("\nAvailable plugins:")
for name, cls in RegistryMeta.get_registry().items():
print(f" - {name}: {cls.__name__}")

# Output:
# [Registry] Registered plugin: emailplugin
# [Registry] Registered plugin: slackplugin
# [Registry] Registered plugin: databaseplugin
#
# Available plugins:
# - emailplugin: EmailPlugin
# - slackplugin: SlackPlugin
# - databaseplugin: DatabasePlugin

# Dynamically instantiate plugins
def load_and_execute(plugin_name: str) -> str:
"""Load a plugin by name and execute it."""
registry = RegistryMeta.get_registry()
if plugin_name not in registry:
raise ValueError(f"Plugin '{plugin_name}' not found. Available: {list(registry.keys())}")

plugin_cls = registry[plugin_name]
instance = plugin_cls()
return instance.execute()

print(f"\nExecuting 'slackplugin': {load_and_execute('slackplugin')}")
# Output: Executing 'slackplugin': Sending to Slack...

Validation Steps:

  1. ✅ Each class definition triggers registration message
  2. ✅ Registry contains all three plugin classes
  3. load_and_execute() finds and instantiates plugins by name
  4. ✅ Unknown plugin names raise clear error

Real-World Use: This is how many Python frameworks discover plugins. Django admin, Flask extensions, pytest plugins—they all use similar registration patterns.

Pattern 3: Singleton Metaclass

The Problem: You want to ensure only one instance of a class exists globally. For example, a database connection pool or configuration object should never be instantiated multiple times.

Example: Singleton Pattern via Metaclass

# Specification Reference: Implement singleton pattern ensuring single instance
# Prompt: Create a metaclass that implements the Singleton pattern (one instance only)

class SingletonMeta(type):
"""Metaclass that implements the Singleton design pattern."""

# Store instances for each class using this metaclass
_instances: dict[type, object] = {}

def __call__(cls, *args, **kwargs) -> object:
"""Intercept instantiation to return existing instance if it exists."""
# If this class doesn't have an instance yet, create one
if cls not in SingletonMeta._instances:
instance = super().__call__(*args, **kwargs)
SingletonMeta._instances[cls] = instance

return SingletonMeta._instances[cls]


class DatabaseConnection(metaclass=SingletonMeta):
"""Global database connection pool (singleton)."""

def __init__(self, host: str = "localhost", port: int = 5432):
"""Initialize connection pool."""
self.host = host
self.port = port
self.connection_id = id(self)
print(f"DatabaseConnection created: {self.host}:{self.port} (ID: {self.connection_id})")

def query(self, sql: str) -> list[dict]:
"""Execute a SQL query."""
return [{"result": f"Query executed: {sql}"}]


# Create multiple "instances"
db1 = DatabaseConnection(host="localhost")
db2 = DatabaseConnection(host="different_host") # Parameters ignored!
db3 = DatabaseConnection()

# All point to the same object
print(f"\ndb1 is db2: {db1 is db2}") # True
print(f"db2 is db3: {db2 is db3}") # True
print(f"id(db1) == id(db2): {id(db1) == id(db2)}") # True

# Verify the connection is truly shared
print(f"\ndb1.host: {db1.host}")
print(f"db2.host: {db2.host}")
print(f"db3.host: {db3.host}")
# All show "localhost" because they're the same object

Validation Steps:

  1. ✅ First instantiation: prints "created" message (constructor runs)
  2. ✅ Subsequent instantiations: return the same object (no "created" message)
  3. db1 is db2 evaluates to True
  4. ✅ All three variables point to same memory address

Important Caveat: While metaclass singletons work, many Python developers prefer simpler approaches like module-level instances or the @functools.lru_cache pattern. Singletons are sometimes considered an anti-pattern because they can make testing difficult (hard to mock).

Pattern 4: Abstract Method Enforcement Metaclass

The Problem: You want to force all subclasses to implement specific methods. While Python's abc.ABCMeta exists for this, let's build our own to understand the pattern.

Example: Framework Base Class with Enforced Methods

# Specification Reference: Enforce abstract methods at class creation
# Prompt: Create a metaclass that raises error if class doesn't implement all abstract methods

class FrameworkMeta(type):
"""Metaclass that enforces all subclasses implement required methods."""

required_methods: list[str] = ['validate', 'execute', 'cleanup']

def __new__(mcs, name: str, bases: tuple, namespace: dict) -> type:
"""Check that required methods are implemented."""
# Skip validation for the base framework class itself
if name == 'FrameworkComponent':
return super().__new__(mcs, name, bases, namespace)

# Check that all required methods exist
missing_methods = [
method_name for method_name in mcs.required_methods
if method_name not in namespace or not callable(namespace[method_name])
]

if missing_methods:
raise NotImplementedError(
f"Class '{name}' must implement: {', '.join(missing_methods)}. "
f"Use: def {missing_methods[0]}(self) -> None: ..."
)

return super().__new__(mcs, name, bases, namespace)


class FrameworkComponent(metaclass=FrameworkMeta):
"""Base class for all framework components."""
pass


# Valid implementation
class FileProcessor(FrameworkComponent):
"""A framework component that processes files."""

def validate(self) -> bool:
"""Validate input is ready."""
return True

def execute(self) -> str:
"""Execute the processing."""
return "File processed"

def cleanup(self) -> None:
"""Clean up resources."""
print("Cleanup done")


# Invalid implementation (missing 'cleanup')
try:
class BadComponent(FrameworkComponent):
def validate(self) -> bool:
return True

def execute(self) -> str:
return "done"
# Missing 'cleanup' — will raise NotImplementedError
except NotImplementedError as e:
print(f"Implementation error: {e}")
# Output: Implementation error: Class 'BadComponent' must implement: cleanup...


# Use the valid component
processor = FileProcessor()
if processor.validate():
result = processor.execute()
processor.cleanup()
print(result)

Validation Steps:

  1. ✅ Valid class with all methods: instantiates successfully
  2. ✅ Invalid class missing method: raises NotImplementedError at class definition
  3. ✅ Error message clearly lists missing methods

Why Not Just Use abc.ABCMeta?: Python already has abstract base classes (abc.ABC). They serve the same purpose. The reason to understand this metaclass pattern is to see how it works internally—and to recognize that sometimes simpler approaches already exist.

Pattern 5: Simplified Django-Like Model Metaclass

The Problem: Django's ORM turns Python class definitions into database table definitions. How? A metaclass that collects field definitions and auto-generates database methods.

Let's build a simplified version to understand the pattern.

Example: Building a Framework-Like Field Collection System

# Specification Reference: Understand Django-like pattern for field collection
# Prompt: Create a simplified version of Django's Model metaclass that collects field definitions

from dataclasses import dataclass, field as dataclass_field
from typing import Any


# First, define a Field descriptor
class Field:
"""Descriptor representing a database field."""

def __init__(self, field_type: type, required: bool = True, default: Any = None):
self.field_type = field_type
self.required = required
self.default = default
self.name: str | None = None # Set by metaclass

def __set_name__(self, owner: type, name: str) -> None:
"""Called when descriptor is assigned to a class attribute."""
self.name = name

def __repr__(self) -> str:
return f"Field({self.field_type.__name__}, required={self.required})"


# Now, the Model metaclass
class ModelMeta(type):
"""Metaclass that collects Field definitions and builds model metadata."""

def __new__(mcs, name: str, bases: tuple, namespace: dict) -> type:
"""Collect all Field instances and create model class."""
# Find all Field instances in the class definition
fields: dict[str, Field] = {}
for attr_name, attr_value in list(namespace.items()):
if isinstance(attr_value, Field):
fields[attr_name] = attr_value
attr_value.name = attr_name

# Store fields metadata on the class
namespace['_fields'] = fields

# Create the class
cls = super().__new__(mcs, name, bases, namespace)

# Auto-generate __init__ (simplified version)
if name != 'Model' and fields:
mcs._generate_init(cls, fields)

return cls

@staticmethod
def _generate_init(cls: type, fields: dict[str, Field]) -> None:
"""Generate __init__ method based on fields."""
def __init__(self, **kwargs):
# Set all field values from kwargs
for field_name, field_obj in fields.items():
if field_name in kwargs:
setattr(self, field_name, kwargs[field_name])
elif field_obj.default is not None:
setattr(self, field_name, field_obj.default)
elif field_obj.required:
raise ValueError(f"Field '{field_name}' is required")

cls.__init__ = __init__


# Base Model class
class Model(metaclass=ModelMeta):
"""Base class for all models."""
_fields: dict[str, Field] = {}


# Define a User model
class User(Model):
"""A User model with fields."""
id = Field(int, required=True)
name = Field(str, required=True)
email = Field(str, required=True)
bio = Field(str, required=False, default="No bio")


# Inspect the model
print("User model fields:")
for field_name, field_obj in User._fields.items():
print(f" {field_name}: {field_obj}")

# Output:
# User model fields:
# id: Field(int, required=True)
# name: Field(str, required=True)
# email: Field(str, required=True)
# bio: Field(str, required=False)

# Create an instance
user = User(id=1, name="Alice", email="[email protected]")
print(f"\nUser created: {user.name} ({user.email})")
print(f"Bio: {user.bio}") # Uses default

# Try creating without required field
try:
bad_user = User(name="Bob") # Missing 'id'
except ValueError as e:
print(f"\nError creating user: {e}")
# Output: Error creating user: Field 'id' is required

Validation Steps:

  1. ✅ Fields collected into _fields dictionary
  2. __init__ auto-generated, accepts field values
  3. ✅ Default values work correctly
  4. ✅ Missing required fields raise clear error
  5. ✅ Model inspectable: User._fields shows all fields

How Django Actually Does It: Django's metaclass is much more complex—it builds SQL, handles relationships, generates database queries, etc. But the core pattern is identical: a metaclass collects metadata from the class definition and uses it to generate functionality.

Pattern 6: Comparing Metaclass vs __init_subclass__ (The Alternative)

The Problem: You want to enforce that all subclasses have a name attribute. But is a metaclass the best approach?

Python 3.6+ introduced __init_subclass__() as a simpler alternative to metaclasses for many use cases.

Example: Validation Using Both Approaches

# Specification Reference: Compare metaclass vs __init_subclass__ alternatives
# Prompt: Show metaclass and __init_subclass__ approaches for same validation problem

# Approach 1: Using a metaclass (more powerful, more complex)
class ValidationMeta(type):
"""Metaclass approach to validation."""

def __new__(mcs, name: str, bases: tuple, namespace: dict) -> type:
if name != 'BaseComponent' and 'name' not in namespace:
raise TypeError(f"Class '{name}' must define 'name' attribute")
return super().__new__(mcs, name, bases, namespace)


class BaseComponent(metaclass=ValidationMeta):
"""Base using metaclass."""
pass


# Approach 2: Using __init_subclass__ (simpler, newer Python feature)
class BaseComponentSimpler:
"""Base using __init_subclass__."""

def __init_subclass__(cls, **kwargs) -> None:
"""Called when a subclass is created."""
super().__init_subclass__(**kwargs)
if 'name' not in cls.__dict__:
raise TypeError(f"Class '{cls.__name__}' must define 'name' attribute")


# Both work identically
class ValidComponent1(BaseComponent):
name = "component_1"

class ValidComponent2(BaseComponentSimpler):
name = "component_2"

print("Both approaches work:")
print(f" Metaclass component: {ValidComponent1.name}")
print(f" __init_subclass__ component: {ValidComponent2.name}")

# Both raise the same error for missing 'name'
try:
class BadComponent1(BaseComponent):
pass # Missing 'name'
except TypeError as e:
print(f"\nMetaclass error: {e}")

try:
class BadComponent2(BaseComponentSimpler):
pass # Missing 'name'
except TypeError as e:
print(f"__init_subclass__ error: {e}")


# COMPARISON TABLE
print("\n" + "="*70)
print("WHEN TO USE WHAT")
print("="*70)
print("""
Use __init_subclass__ when:
✅ You want to validate or process subclasses (simpler, newer Python)
✅ You need access to subclass at creation time
✅ Readability is important (more obvious to future maintainers)

Use Metaclasses when:
✅ You need to customize class instantiation (__call__)
✅ You need to modify the entire class hierarchy
✅ You're building a framework (Django-level complexity)
✅ You need to intercept before __init_subclass__ is available
""")

Key Insight: __init_subclass__() solves 80% of metaclass use cases with simpler, more readable code. Use __init_subclass__ first; only reach for metaclasses when you have a specific framework-level reason.

Pattern 7: The __prepare__() Method (Advanced)

For completeness, let's mention __prepare__() without a deep dive (since the spec marks it "mention only").

What __prepare__() does: It lets you customize the namespace dictionary used during class creation.

Example Use Case (for reference, not required):

# Example: __prepare__() customizes the namespace before class body executes
class CustomNamespaceMeta(type):
"""Metaclass that provides custom namespace."""

@classmethod
def __prepare__(mcs, name: str, bases: tuple, **kwargs) -> dict:
"""Return a custom dict-like object as the class namespace."""
print(f"__prepare__ called for class '{name}'")
# Could return an OrderedDict, defaultdict, or custom class
return {'_custom_prepared': True}

def __new__(mcs, name: str, bases: tuple, namespace: dict) -> type:
print(f"__new__ called for class '{name}'")
return super().__new__(mcs, name, bases, namespace)


class MyClass(metaclass=CustomNamespaceMeta):
x = 1 # This assignment goes into the custom namespace


print(MyClass._custom_prepared) # True
# Output:
# __prepare__ called for class 'MyClass'
# __new__ called for class 'MyClass'
# True

Why mention it?: Advanced frameworks like Cython use __prepare__() to customize the class creation process. You might encounter it reading framework source code, but you rarely need to use it in application code.

Real-World Framework Patterns

How Django's Model Metaclass Works

Django's ORM uses a metaclass to turn Python field definitions into database table definitions:

# Simplified Django pattern (how it actually works)
class Model(metaclass=ModelBase):
"""Base class for all Django models."""
pass

class User(Model):
"""Example Django model."""
id = IntegerField(primary_key=True)
name = CharField(max_length=100)
email = EmailField()

# The ModelBase metaclass:
# 1. Collects all Field instances
# 2. Creates database table schema
# 3. Generates SQL queries
# 4. Creates manager object (User.objects.all())

How SQLAlchemy's declarative_base() Works

SQLAlchemy uses a metaclass-based system for ORM:

from sqlalchemy.orm import declarative_base

Base = declarative_base() # Creates base with metaclass

class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)

# The metaclass:
# 1. Detects table name
# 2. Maps Python types to SQL types
# 3. Creates relationship handlers

Why Real Frameworks Use Metaclasses: When you're building a framework serving thousands of developers, metaclasses let you provide a clean, intuitive API while handling complexity behind the scenes. Framework developers accept the complexity because it's paid once and benefits millions of users.

When NOT to Use Metaclasses

This is critical: Don't use metaclasses just because you learned about them.

Red Flags (don't reach for metaclasses):

  • ❌ "I need to add a method to all instances" → Use inheritance instead
  • ❌ "I want to validate input data" → Use __init__() or __post_init__() instead
  • ❌ "I want to log method calls" → Use a decorator instead
  • ❌ "I want to control when a class is created" → Use __init_subclass__() instead

A rule of thumb: If a simple decorator, __init_subclass__(), or class inheritance solves your problem, use that. Metaclasses are a last resort—they make code complex and hard to debug.

Common Pitfalls in Metaclass Patterns

  1. Forgetting super(): Always call super().__new__() or super().__init__()
  2. Confusing instances and classes: "Instance of a metaclass" is a class; "instance of a class" is an object
  3. MRO confusion: When mixing multiple metaclasses, method resolution can be tricky
  4. Performance overhead: Metaclasses add overhead to every class definition
  5. Testing difficulty: Metaclass behavior is hard to mock in tests

Summary & Key Takeaways

You now understand:

  1. Validation metaclasses enforce class structure at definition time
  2. Registration metaclasses auto-collect classes in a central registry
  3. Singleton metaclasses enforce single-instance patterns
  4. Abstract enforcement validates method implementation
  5. Field collection (Django pattern) builds metadata from class definitions
  6. __init_subclass__ is a simpler alternative for most use cases
  7. __prepare__() customizes the class namespace (advanced, rarely needed)
  8. Real frameworks (Django, SQLAlchemy) use metaclasses for clean APIs
  9. Complexity tradeoff: Metaclasses are worth it for frameworks, not for app code
  10. Design decision: Always ask "Is there a simpler approach?" before using a metaclass

The biggest insight: Metaclasses exist to make building frameworks easier, not to make writing applications easier. For application code, use simpler approaches.

Try With AI

Now it's time to apply metaclass patterns to problems. You'll use your AI companion to help you understand framework patterns and make architectural decisions.

Your AI Tool: Use ChatGPT web (if you haven't set up a CLI companion yet) or your installed AI tool (Claude Code, Gemini CLI).

Prompt Set: Metaclass Patterns (Bloom's Progression)

Prompt 1: Recall — Registration Pattern Explanation

Ask your AI:

"I want to build a plugin system where plugins auto-register when defined.
Explain how a registration metaclass would work. Include:
1. Where the registry is stored
2. When registration happens (class definition vs instantiation)
3. How to retrieve registered plugins"

Expected Response:

  • Explains registry is class-level attribute on metaclass
  • Clarifies registration happens at class definition time
  • Shows how to access registry (e.g., Meta.registry)
  • Mentions examples like load_and_execute(plugin_name)

How to Validate:

  • Does the explanation trace through the flow clearly?
  • Does it distinguish class definition time from instance creation?
  • Could you implement it based on this explanation?

Prompt 2: Understand — Django's Field Collection Pattern

Ask your AI:

"How does Django's Model metaclass work? Specifically:
1. How does it find Field definitions in a class?
2. What happens to those fields?
3. How does it auto-generate __init__ and other methods?"

Expected Response:

  • Explains iterating through class __dict__ to find Field instances
  • Describes storing fields in _fields or similar
  • Mentions auto-generating __init__, __repr__, SQL queries
  • References descriptor protocol or similar mechanism

How to Validate:

  • Can you sketch how to collect fields?
  • Does the explanation help you understand real Django code?

Prompt 3: Apply — Create a Logging Metaclass

Ask your AI:

"Create a metaclass that logs every method call on a class.
When any method is called, it should print:
'[LOG] ClassName.method_name() called'

Show me:
1. The metaclass implementation
2. How to use it with a test class
3. An example output when methods are called"

Expected Response:

  • Metaclass that wraps all methods
  • Uses __getattribute__() or decorates methods
  • Shows usage: class MyClass(metaclass=LoggingMeta):
  • Demonstrates log output when methods are called

How to Validate:

  • Copy and run the code
  • Define a class with a few methods
  • Call methods and verify logging output appears

Prompt 4: Evaluate — Metaclass vs Alternative Approaches

Ask your AI:

"I want to ensure all methods in a class have docstrings.
Should I use:
A) A metaclass
B) A class decorator
C) __init_subclass__
D) Something else?

Show me code for the best approach, and explain why it's better
than the other options."

Expected Response:

  • Likely recommends decorator or __init_subclass__ as simpler
  • Shows metaclass as valid but more complex
  • Explains tradeoffs (readability, maintainability, framework needs)
  • Demonstrates code for preferred approach

How to Validate:

  • Does the explanation justify the choice?
  • Do you agree with the tradeoff assessment?
  • Could you explain this choice to another developer?

Safety & Ethical Use

As you explore with AI:

  • Test on small examples — Metaclass behavior can be surprising. Test thoroughly before applying to production code.
  • Ask about simpler alternatives — Always ask "Is there a decorator or __init_subclass__ that would work?"
  • Review generated code carefully — Complex metaclass code should be understood, not just copied.
  • Watch for performance impact — Metaclass overhead can accumulate in large systems.

Next Steps (Self-Directed)

After working through these prompts:

  1. Read framework code: Find Django's ModelBase or SQLAlchemy's DeclarativeMeta on GitHub. Can you identify the patterns you learned?
  2. Build a simple framework: Create your own plugin system or field-collection system using a metaclass
  3. Compare approaches: Take a problem you'd solve with a metaclass and rewrite it using __init_subclass__(). Which is clearer?

You've now seen behind the curtain of how frameworks work. The next lesson shifts gears completely: from the powerful-but-specialized metaclass to the practical-and-daily dataclass.