# 44. Classes and Metaclasses

# 44. Classes and Metaclasses

A class is a runtime object that creates instances, stores attributes, participates in inheritance, and defines behavior through the Python object model. In CPython, a class is an object whose type is usually `type`.

A metaclass is the class of a class. It controls how class objects are created, initialized, represented, and called.

For normal classes:

```python id="g3fkzi"
class User:
    pass

print(type(User))
```

Output:

```text id="iqt3cc"
<class 'type'>
```

This means `User` is an object, and its type is `type`.

## 44.1 Classes Are Objects

A class definition creates a class object.

```python id="8zc5m6"
class User:
    name = "anonymous"

    def hello(self):
        return "hello"
```

After execution, `User` is a normal name bound in the surrounding namespace.

```python id="rx6c1m"
print(User)
print(type(User))
print(User.__name__)
print(User.__dict__)
```

The class object stores attributes such as:

```text id="xxewj0"
__name__
__qualname__
__module__
__dict__
__bases__
__mro__
methods
descriptors
class variables
annotations
```

A class is not just syntax. It is a runtime object produced by executing a class statement.

## 44.2 Class Definition Is Execution

A class body is executable code.

```python id="938wkx"
class Example:
    print("inside class body")
    x = 1 + 2
```

Output during definition:

```text id="jiq7he"
inside class body
```

The class body runs immediately when CPython executes the `class` statement. It is not delayed until an instance is created.

This matters because any top-level code inside the class body runs at class creation time:

```python id="aj5kyg"
class Bad:
    data = load_large_file()
```

That work happens when the class is defined.

## 44.3 The Class Creation Pipeline

A class definition:

```python id="yzw9ss"
class User(Base):
    x = 1

    def hello(self):
        return "hello"
```

is conceptually similar to:

```python id="g55w7a"
namespace = {}
namespace["x"] = 1
namespace["hello"] = function_object
User = type("User", (Base,), namespace)
```

The real process has more steps:

```text id="zd6xb1"
1. Evaluate base classes.
2. Determine the metaclass.
3. Ask the metaclass for a class namespace.
4. Execute the class body in that namespace.
5. Create the class object.
6. Call descriptor __set_name__ methods.
7. Call subclass initialization hooks.
8. Bind the class object to its name.
```

This pipeline explains how metaclasses, descriptors, decorators, and inheritance interact.

## 44.4 Evaluating Base Classes

In:

```python id="zu9u35"
class User(Model):
    pass
```

CPython first evaluates `Model`.

Base classes are expressions:

```python id="fnc7gv"
class User(get_base_class()):
    pass
```

The function call happens before the class object is created.

Multiple bases are evaluated left to right:

```python id="qtk75l"
class C(A(), B()):
    pass
```

The resulting objects must be valid base classes or be transformable through `__mro_entries__`.

## 44.5 `__mro_entries__`

The hook `__mro_entries__` allows non-class base objects to replace themselves during class creation.

This is used by some typing and generic machinery.

Example shape:

```python id="esjxp2"
class BaseAlias:
    def __mro_entries__(self, bases):
        return (RealBase,)

class C(BaseAlias()):
    pass
```

Conceptually, CPython turns:

```python id="154gd3"
class C(BaseAlias()):
    pass
```

into:

```python id="kfk2ta"
class C(RealBase):
    pass
```

for inheritance purposes.

Most application code never implements `__mro_entries__`, but it is part of class creation.

## 44.6 Determining the Metaclass

The metaclass can be specified explicitly:

```python id="o3wmta"
class User(metaclass=Meta):
    pass
```

If no metaclass is specified, CPython derives it from the base classes.

For a normal class:

```python id="oe50zn"
class User:
    pass
```

the metaclass is:

```text id="ythfx3"
type
```

For a subclass:

```python id="t5oe3k"
class Child(Base):
    pass
```

the metaclass is usually `type(Base)` or a compatible derived metaclass.

The selected metaclass must be compatible with the metaclasses of all base classes. Otherwise CPython raises a metaclass conflict.

## 44.7 Metaclass Conflict

A metaclass conflict happens when base classes require incompatible metaclasses.

Example:

```python id="dk85qy"
class MetaA(type):
    pass

class MetaB(type):
    pass

class A(metaclass=MetaA):
    pass

class B(metaclass=MetaB):
    pass

class C(A, B):
    pass
```

This raises an error because CPython cannot choose a single metaclass that is compatible with both `MetaA` and `MetaB`.

The usual fix is to define a combined metaclass:

```python id="7bm0x5"
class MetaC(MetaA, MetaB):
    pass

class C(A, B, metaclass=MetaC):
    pass
```

Metaclass conflicts are common when combining frameworks that use metaclasses.

## 44.8 Preparing the Class Namespace

Before executing the class body, CPython asks the metaclass for a namespace.

It does this by calling `__prepare__` if present.

```python id="0o8dg6"
class Meta(type):
    @classmethod
    def __prepare__(mcls, name, bases, **kwargs):
        return {}

class User(metaclass=Meta):
    x = 1
```

The returned object is used as the local namespace for the class body.

Historically, this enabled ordered class namespaces. Modern dictionaries preserve insertion order, but `__prepare__` still supports custom namespace behavior.

Example use cases:

```text id="rq5a86"
tracking declaration order
rejecting duplicate names
collecting field definitions
custom class DSLs
framework model declarations
```

## 44.9 Class Body Namespace

The class body executes with its own local namespace.

```python id="4q8zlf"
x = "global"

class Example:
    x = "class local"
    y = x

print(Example.x)
print(Example.y)
```

Output:

```text id="7d4l4l"
class local
class local
```

Assignments in the class body write into the class namespace, not into an instance.

Function bodies inside the class do not automatically capture class-local names:

```python id="kkxro6"
class Example:
    x = 10

    def method(self):
        return x
```

This usually fails at runtime unless there is a global `x`, because method global lookup uses the module globals, not the class namespace.

Correct:

```python id="mjh0gx"
class Example:
    x = 10

    def method(self):
        return self.x
```

or:

```python id="73a9fm"
class Example:
    x = 10

    def method(self):
        return type(self).x
```

## 44.10 Creating the Class Object

After executing the class body, CPython calls the metaclass.

For a normal class, this means calling `type`.

Conceptually:

```python id="y5jzez"
User = type("User", bases, namespace)
```

The call to `type` creates a `PyTypeObject` internally.

The resulting class object stores:

```text id="q6gq90"
class name
base classes
method resolution order
class dictionary
type flags
slot tables
weakref support
instance layout
descriptor information
subclass relationships
```

A user-defined class is therefore a type object.

## 44.11 `type(name, bases, namespace)`

You can create classes manually with `type`.

```python id="swqmpu"
def hello(self):
    return "hello"

User = type("User", (), {"hello": hello})

u = User()
print(u.hello())
```

This is equivalent in spirit to:

```python id="owh4rx"
class User:
    def hello(self):
        return "hello"
```

The `class` statement is syntax for a structured class creation protocol.

## 44.12 Metaclass `__new__`

A metaclass can customize class creation by overriding `__new__`.

```python id="55v7xr"
class Meta(type):
    def __new__(mcls, name, bases, namespace, **kwargs):
        print("creating", name)
        return super().__new__(mcls, name, bases, namespace)

class User(metaclass=Meta):
    pass
```

Output:

```text id="xsdbla"
creating User
```

`__new__` receives the class name, base classes, and namespace before the class object exists.

Use `__new__` when you need to change the class before creation:

```text id="e80nxe"
modify namespace
validate definitions
inject methods
collect metadata
change base classes
control class object allocation
```

## 44.13 Metaclass `__init__`

A metaclass can customize class initialization by overriding `__init__`.

```python id="o0xfzo"
class Meta(type):
    def __init__(cls, name, bases, namespace, **kwargs):
        print("initializing", name)
        super().__init__(name, bases, namespace)

class User(metaclass=Meta):
    pass
```

`__init__` receives the already created class object as `cls`.

Use metaclass `__init__` when you need to register or inspect the class after creation:

```text id="u1q6kk"
register subclasses
validate final class
attach metadata
update external registries
```

## 44.14 Metaclass `__call__`

Calling a class is controlled by its metaclass.

For a normal class:

```python id="kq4rc6"
u = User("Ada")
```

is handled by:

```text id="y1lk7w"
type(User).__call__(User, "Ada")
```

For normal metaclasses, `type.__call__` performs:

```text id="vxizzk"
1. call User.__new__(User, ...)
2. if result is an instance of User, call User.__init__(instance, ...)
3. return instance
```

A metaclass can override `__call__`:

```python id="ce2m4g"
class SingletonMeta(type):
    def __call__(cls, *args, **kwargs):
        if not hasattr(cls, "_instance"):
            cls._instance = super().__call__(*args, **kwargs)
        return cls._instance

class Config(metaclass=SingletonMeta):
    pass
```

Now every `Config()` call returns the same object.

Use this sparingly. Metaclass `__call__` changes instance creation globally for the class.

## 44.15 Instance Creation

For a normal class:

```python id="6kcvu5"
class User:
    def __new__(cls, name):
        print("__new__")
        return super().__new__(cls)

    def __init__(self, name):
        print("__init__")
        self.name = name

u = User("Ada")
```

Output:

```text id="868s0q"
__new__
__init__
```

`__new__` creates the object.

`__init__` initializes the object.

`__new__` is a static-like constructor. It receives the class and returns an object.

`__init__` receives the created instance and should return `None`.

## 44.16 Class Dictionaries

A class exposes its namespace through `__dict__`.

```python id="zk9rbi"
class User:
    kind = "human"

    def hello(self):
        return "hello"

print(User.__dict__)
```

`User.__dict__` is usually a read-only mapping proxy.

```python id="pj2w2d"
print(type(User.__dict__))
```

You cannot assign directly into the mapping proxy:

```python id="sgmmu2"
User.__dict__["x"] = 1
```

But you can assign attributes on the class:

```python id="4utuvq"
User.x = 1
```

This updates the underlying class dictionary through type machinery.

## 44.17 Class Variables

A class variable is stored on the class object.

```python id="hkk4e1"
class Counter:
    count = 0
```

Access through the class:

```python id="kts1o0"
print(Counter.count)
```

Access through an instance:

```python id="8w340h"
c = Counter()
print(c.count)
```

If the instance does not have `count`, lookup finds it on the class.

Assignment through the instance creates or updates an instance attribute:

```python id="d1dq8x"
c.count = 10

print(c.__dict__)
print(Counter.count)
```

Output:

```text id="frwv5a"
{'count': 10}
0
```

This is a common source of bugs with mutable class variables.

## 44.18 Mutable Class Variable Pitfall

```python id="x8s3te"
class Bag:
    items = []

    def add(self, item):
        self.items.append(item)
```

Usage:

```python id="81dg5u"
a = Bag()
b = Bag()

a.add("x")
print(b.items)
```

Output:

```text id="kqxpp6"
['x']
```

Both instances share the same class-level list.

Correct design:

```python id="l9yqqx"
class Bag:
    def __init__(self):
        self.items = []

    def add(self, item):
        self.items.append(item)
```

Use class variables for shared constants or intentional shared state. Use instance variables for per-instance state.

## 44.19 Instance Dictionaries

Normal user-defined objects have an instance dictionary.

```python id="8j4hxd"
class User:
    pass

u = User()
u.name = "Ada"

print(u.__dict__)
```

Output:

```text id="e5beij"
{'name': 'Ada'}
```

Attribute assignment stores values in the instance dictionary unless a data descriptor intercepts the assignment.

This is why Python objects are flexible by default. New attributes can be added dynamically.

## 44.20 `__slots__` and Instance Layout

A class can define `__slots__` to use fixed attribute slots instead of a normal instance dictionary.

```python id="ok0zm7"
class Point:
    __slots__ = ("x", "y")

    def __init__(self, x, y):
        self.x = x
        self.y = y
```

Instances of `Point` store `x` and `y` in fixed slots.

```python id="3bbvk2"
p = Point(1, 2)

print(hasattr(p, "__dict__"))
```

Usually:

```text id="mk5rwc"
False
```

Slots create descriptors on the class:

```python id="1l1jy7"
print(Point.__dict__["x"])
print(Point.__dict__["y"])
```

These descriptors read and write fixed storage locations.

## 44.21 Inheritance

A class can inherit from one or more base classes.

```python id="9jq7to"
class Animal:
    def speak(self):
        return "..."

class Dog(Animal):
    def speak(self):
        return "woof"
```

The subclass stores its base classes in `__bases__`.

```python id="s4yqfg"
print(Dog.__bases__)
```

The method resolution order is stored in `__mro__`.

```python id="zfn7t5"
print(Dog.__mro__)
```

Attribute lookup follows the MRO.

## 44.22 Method Resolution Order

For:

```python id="z3o721"
class A:
    def f(self):
        return "A"

class B(A):
    pass

class C(B):
    pass
```

`C().f()` finds `f` in `A` through the MRO.

```python id="km9ffo"
print(C.__mro__)
```

Output shape:

```text id="mvct9m"
(<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
```

The MRO is computed when the class is created. CPython uses C3 linearization for new-style classes.

## 44.23 Multiple Inheritance

Python supports multiple inheritance.

```python id="vr4scb"
class A:
    def f(self):
        return "A"

class B:
    def f(self):
        return "B"

class C(A, B):
    pass

print(C().f())
print(C.__mro__)
```

The leftmost base usually has priority, subject to the C3 MRO rules.

Output:

```text id="njnz6d"
A
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
```

Multiple inheritance is powerful, but it requires cooperative design when classes share methods and initialization paths.

## 44.24 Cooperative `super`

`super()` follows the MRO.

```python id="xgd0er"
class A:
    def f(self):
        return "A"

class B(A):
    def f(self):
        return "B" + super().f()

class C(A):
    def f(self):
        return "C" + super().f()

class D(B, C):
    def f(self):
        return "D" + super().f()

print(D().f())
print(D.__mro__)
```

Output:

```text id="f6z3iu"
DBCA
```

`super()` does not mean “call my parent.” It means “continue the MRO after the current class.”

This is essential in multiple inheritance.

## 44.25 Class Decorators

A class decorator receives the class object after it is created.

```python id="6z5a86"
def register(cls):
    registry[cls.__name__] = cls
    return cls

registry = {}

@register
class User:
    pass
```

This is roughly:

```python id="alod77"
class User:
    pass

User = register(User)
```

Class decorators are often simpler than metaclasses. Use a metaclass only when class creation itself must be controlled across a family of classes.

## 44.26 `__init_subclass__`

A base class can define `__init_subclass__` to run code when it is subclassed.

```python id="zfxjod"
class Model:
    registry = []

    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        Model.registry.append(cls)

class User(Model):
    pass

class Post(Model):
    pass

print(Model.registry)
```

This is often simpler than a metaclass for subclass registration.

Use `__init_subclass__` for:

```text id="yp6gp0"
subclass registration
subclass validation
default subclass configuration
lightweight framework hooks
```

## 44.27 `__set_name__` During Class Creation

After creating a class, CPython calls `__set_name__` on descriptors in the class namespace.

```python id="5vzb6d"
class Field:
    def __set_name__(self, owner, name):
        print(owner, name)

class User:
    id = Field()
    name = Field()
```

Output shape:

```text id="vzb7xd"
<class '__main__.User'> id
<class '__main__.User'> name
```

This lets descriptors discover the class and attribute name they are assigned to.

The ordering is:

```text id="4ow51f"
class body executes
class object is created
descriptor __set_name__ hooks run
__init_subclass__ hooks run on bases
class decorators run
class name is bound
```

## 44.28 Metaclasses vs Class Decorators vs `__init_subclass__`

| Mechanism | Runs | Best for |
|---|---|---|
| Metaclass | During class creation | Deep control over class object creation |
| Class decorator | After class creation | One-off transformation or registration |
| `__init_subclass__` | When subclass is created | Base-class-driven subclass hooks |
| Descriptor `__set_name__` | During class finalization | Field name discovery |

Prefer the simplest mechanism that solves the problem.

For most code:

```text id="wd9e73"
class decorator > __init_subclass__ > metaclass
```

A metaclass is justified when you need to customize namespace preparation, class allocation, metaclass-level methods, class calling, or enforce rules across a class hierarchy.

## 44.29 Metaclass Example: Enforcing Required Attributes

```python id="p13a12"
class RequireTableName(type):
    def __new__(mcls, name, bases, namespace):
        cls = super().__new__(mcls, name, bases, namespace)

        if bases and not hasattr(cls, "table_name"):
            raise TypeError(f"{name} must define table_name")

        return cls

class Model(metaclass=RequireTableName):
    pass

class User(Model):
    table_name = "users"
```

This metaclass validates subclasses.

This can also be implemented with `__init_subclass__`, which is often simpler:

```python id="8wx9md"
class Model:
    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        if not hasattr(cls, "table_name"):
            raise TypeError(f"{cls.__name__} must define table_name")
```

Use the base-class hook unless the metaclass gives a concrete advantage.

## 44.30 Metaclass Example: Registry

```python id="q07d17"
class RegistryMeta(type):
    registry = {}

    def __init__(cls, name, bases, namespace):
        super().__init__(name, bases, namespace)
        if name != "Base":
            RegistryMeta.registry[name] = cls

class Base(metaclass=RegistryMeta):
    pass

class User(Base):
    pass

class Post(Base):
    pass

print(RegistryMeta.registry)
```

This records subclasses as they are created.

Again, `__init_subclass__` may be enough:

```python id="byf0x9"
class Base:
    registry = {}

    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        Base.registry[cls.__name__] = cls
```

Metaclasses should be used when they simplify the system, not when they merely feel powerful.

## 44.31 Metaclass Example: Custom Namespace

A metaclass can reject duplicate attribute names by returning a custom namespace from `__prepare__`.

```python id="9r9h1q"
class NoDuplicateDict(dict):
    def __setitem__(self, key, value):
        if key in self:
            raise TypeError(f"duplicate name: {key}")
        super().__setitem__(key, value)

class NoDuplicateMeta(type):
    @classmethod
    def __prepare__(mcls, name, bases):
        return NoDuplicateDict()

class Example(metaclass=NoDuplicateMeta):
    x = 1
    y = 2
```

If `x` were assigned twice, class creation would fail.

This is one case where a metaclass is appropriate because only the metaclass can customize the class-body namespace before execution.

## 44.32 Built-in Classes and Heap Types

CPython has both statically defined built-in types and dynamically allocated heap types.

Examples of built-in types:

```python id="bsov46"
int
str
list
dict
tuple
type
object
```

Many built-in types are defined in C.

User-defined classes are heap types created at runtime.

Both participate in the same object model:

```python id="fbhr47"
print(type(list))
print(type(object))
print(type(type))
```

Output:

```text id="errhki"
<class 'type'>
<class 'type'>
<class 'type'>
```

Even built-in classes are objects.

## 44.33 `object` and `type`

The relationship between `object` and `type` is central.

```python id="ls93k5"
print(isinstance(object, type))
print(isinstance(type, object))
print(type(type))
print(type(object))
```

Important facts:

```text id="9186px"
object is the base class of most Python objects
type is the default metaclass of classes
type is an instance of itself
object is an instance of type
type inherits from object
```

This circular-looking structure is carefully bootstrapped inside CPython.

It allows classes to be objects while still having a root inheritance hierarchy.

## 44.34 CPython Type Objects

At the C level, a class object is represented by a type object, commonly `PyTypeObject`.

A type object contains:

```text id="pj1kt2"
object header
type name
basic instance size
item size for variable-sized objects
method table
slot functions
base classes
MRO
dictionary
flags
allocation functions
deallocation functions
attribute access functions
call behavior
numeric, sequence, and mapping operation tables
```

This is why classes control object behavior. The class object contains the function pointers and metadata used by the runtime.

## 44.35 Type Slots

Python special methods often correspond to C-level slots.

Examples:

| Python method | Runtime operation |
|---|---|
| `__len__` | length operation |
| `__getitem__` | indexing |
| `__setitem__` | item assignment |
| `__iter__` | iteration |
| `__next__` | iterator next |
| `__call__` | call |
| `__add__` | numeric addition |
| `__getattribute__` | attribute lookup |
| `__new__` | allocation |
| `__init__` | initialization |

When a class is created, CPython builds slot tables from special methods.

This lets bytecode and C APIs call operations efficiently without performing a normal Python attribute lookup every time.

## 44.36 Special Method Lookup

Special method lookup often bypasses instance dictionaries.

Example:

```python id="w8gu52"
class Example:
    pass

e = Example()
e.__len__ = lambda: 10

len(e)
```

This raises `TypeError`, because `len(e)` looks for `__len__` on the type, not as an arbitrary instance attribute.

Correct:

```python id="a41p2c"
class Example:
    def __len__(self):
        return 10

e = Example()
print(len(e))
```

Special methods belong on the class so CPython can populate and use type slots.

## 44.37 Attribute Lookup for Classes

For instance access:

```python id="ars6ez"
obj.attr
```

CPython searches the instance and its class hierarchy.

For class access:

```python id="e6vx8d"
Class.attr
```

CPython searches the class object and its metaclass.

Example:

```python id="511n1a"
class Meta(type):
    label = "meta"

class User(metaclass=Meta):
    label = "class"

print(User.label)
```

Output:

```text id="8dznft"
class
```

If `label` is absent from `User`, lookup may find it on `Meta`.

Metaclasses define behavior and attributes of class objects.

## 44.38 Metaclass Methods

A metaclass method is a method on the class object.

```python id="7avylj"
class Meta(type):
    def describe(cls):
        return cls.__name__

class User(metaclass=Meta):
    pass

print(User.describe())
```

Here, `describe` is found on `Meta` and bound to `User`.

This is analogous to how instance methods are found on `User` and bound to `user_instance`.

```text id="dm9zxe"
instance method:
    User.method -> bound to instance

metaclass method:
    Meta.method -> bound to class object
```

## 44.39 Classes and Descriptors

Class dictionaries store descriptors.

```python id="jcw8ig"
class User:
    @property
    def name(self):
        return "Ada"

print(User.__dict__["name"])
```

When an instance accesses `name`, descriptor logic runs.

```python id="bochjq"
u = User()
print(u.name)
```

Methods are descriptors too:

```python id="yrosvm"
class User:
    def hello(self):
        return "hello"

u = User()
print(u.hello)
```

The function stored in `User.__dict__` binds to `u` through its descriptor `__get__`.

## 44.40 Classes and Garbage Collection

Class objects can participate in reference cycles.

Examples:

```text id="zbbqva"
class object references methods
methods reference globals
functions reference code objects
closures may reference class-related state
instances reference class
class may reference descriptors
descriptors may reference owner class
```

CPython’s cyclic garbage collector can collect unreachable classes and related objects if they are not kept alive by other references.

Most ordinary classes live until process exit because modules keep references to them.

```python id="25zzxi"
class User:
    pass
```

The module dictionary holds `User`.

## 44.41 Classes and Modules

A class records the module where it was defined.

```python id="23z7po"
class User:
    pass

print(User.__module__)
```

Usually:

```text id="btn8n6"
__main__
```

or the module name.

This affects representation, pickling, documentation, and introspection.

A class does not physically belong to a module. The module dictionary merely holds a reference to the class object.

```python id="vxme1s"
OtherName = User
```

Now the same class object has another name.

## 44.42 Class Identity

Class identity is object identity.

```python id="05l12d"
class User:
    pass

A = User
B = User

print(A is B)
```

Output:

```text id="krnpze"
True
```

If a module is imported twice under different names, class objects may be duplicated.

```text id="59akdg"
package.models.User
models.User
```

These may be different class objects even if they came from the same file.

That breaks `isinstance`, registry lookup, serialization, and singleton assumptions.

## 44.43 Class Annotations

Class annotations are stored in `__annotations__`.

```python id="qmsv40"
class User:
    id: int
    name: str = "anonymous"

print(User.__annotations__)
```

Output:

```text id="fzxf79"
{'id': <class 'int'>, 'name': <class 'str'>}
```

Annotations do not automatically create instance fields.

```python id="pveplo"
u = User()
print(hasattr(u, "id"))
```

Usually:

```text id="lxeohq"
False
```

Frameworks such as dataclasses, attrs, Pydantic, and ORMs inspect annotations to generate behavior.

## 44.44 Dataclasses as Class Transformation

`dataclasses.dataclass` is a class decorator.

```python id="r4xqnl"
from dataclasses import dataclass

@dataclass
class User:
    id: int
    name: str
```

It receives the class object, inspects annotations, and adds methods such as:

```text id="i9m60v"
__init__
__repr__
__eq__
```

This shows that major class behavior can be added after class creation without a metaclass.

## 44.45 Abstract Base Classes

The `abc` module uses metaclass machinery.

```python id="cudwvv"
from abc import ABC, abstractmethod

class Store(ABC):
    @abstractmethod
    def get(self, key):
        pass
```

`ABC` uses `ABCMeta`, a metaclass that tracks abstract methods and prevents instantiation until they are implemented.

```python id="j5b315"
class BadStore(Store):
    pass

BadStore()
```

This raises `TypeError`.

Abstract base classes are a practical example of metaclasses enforcing class contracts.

## 44.46 Classes and `isinstance`

`isinstance(obj, cls)` usually checks whether `type(obj)` is `cls` or a subclass of `cls`.

```python id="3almnz"
class Animal:
    pass

class Dog(Animal):
    pass

d = Dog()

print(isinstance(d, Dog))
print(isinstance(d, Animal))
```

Metaclasses can customize this through `__instancecheck__`.

ABC machinery uses this for virtual subclass behavior.

## 44.47 Classes and `issubclass`

`issubclass(A, B)` checks whether class `A` is a subclass of class `B`.

```python id="3tkb0g"
class A:
    pass

class B(A):
    pass

print(issubclass(B, A))
```

Metaclasses can customize this through `__subclasscheck__`.

This is another place where class behavior is mediated by metaclass logic.

## 44.48 Dynamic Class Creation

Dynamic class creation is useful in frameworks and code generation.

```python id="pq2ye4"
def make_model(name, fields):
    namespace = {"__annotations__": fields}
    return type(name, (), namespace)

User = make_model("User", {"id": int, "name": str})

print(User)
print(User.__annotations__)
```

Dynamic classes should still follow ordinary class rules:

```text id="kwqh7u"
valid name
clear module
stable identity
predictable bases
explicit public API
```

Set `__module__` when needed:

```python id="hyvbmw"
namespace = {
    "__module__": __name__,
    "__annotations__": fields,
}
```

This improves introspection and serialization.

## 44.49 When to Use a Metaclass

Use a metaclass when you need one of these:

```text id="j2iimq"
custom class namespace through __prepare__
control over class allocation through __new__
metaclass-level methods or properties
custom class call behavior
deep integration across a class hierarchy
framework-level class validation
special instance or subclass checks
```

Avoid metaclasses for simple registration, validation, or method generation when class decorators or `__init_subclass__` are enough.

Metaclasses are global to a class hierarchy. They compose poorly when unrelated libraries define different metaclasses.

## 44.50 Key Points

A class is a runtime object.

Most classes are instances of `type`.

A metaclass is the type of a class.

Class definitions execute a body, collect a namespace, and call a metaclass to create the class object.

Instances are created by calling a class, which is handled by the metaclass `__call__`.

Class dictionaries store methods, descriptors, annotations, and class variables.

Inheritance uses the method resolution order.

Special methods are installed into type slots and are usually looked up on the class, not the instance.

Descriptors, `__set_name__`, `__init_subclass__`, class decorators, and metaclasses all participate in class construction.

Use metaclasses only when simpler class mechanisms cannot express the required behavior.
