Python types.new_class: Dynamic Classes

Published on: August 30, 2026
Reading time: 3 minutes
A person typing on a laptop with a Python programming book visible, capturing technology and learning.

types.new_class() creates classes dynamically while following the core rules of a normal class statement. It selects the appropriate metaclass, prepares the class namespace, and runs a callback that inserts attributes, methods, and metadata before the final type is built.

It is useful in frameworks, ORMs, model generators, plugin systems, and schema-driven APIs. For simple cases, type() may be enough; new_class is valuable when metaclasses, __prepare__, or dynamic bases matter.

Signature

types.new_class(name, bases=(), kwds=None, exec_body=None)

name sets the class name, bases contains base classes, kwds represents class-header options, and exec_body receives the prepared namespace.

Basic example

from types import new_class

def populate(namespace):
    namespace["category"] = "dynamic"

    def describe(self):
        return self.category

    namespace["describe"] = describe

Product = new_class("Product", (), {}, populate)
print(Product().describe())

The callback modifies the namespace before class creation.

Conceptual equivalent

class Product:
    category = "dynamic"

    def describe(self):
        return self.category

A normal declaration is preferable when the structure is known while writing the program. Dynamic generation should be reserved for definitions determined at runtime.

Using a base class

class Model:
    def save(self):
        print("saving", type(self).__name__)

def body(ns):
    ns["table"] = "customers"

Customer = new_class("Customer", (Model,), exec_body=body)

The generated class participates normally in inheritance, MRO, descriptors, and super().

Explicit metaclass

class Meta(type):
    def __new__(mcls, name, bases, namespace):
        namespace["registered"] = True
        return super().__new__(mcls, name, bases, namespace)

Generated = new_class(
    "Generated",
    (),
    {"metaclass": Meta},
    lambda ns: ns.update(value=10),
)

The kwds dictionary corresponds to class-header keywords. The metaclass option participates in class creation rather than becoming an ordinary class attribute.

__prepare__ and custom namespaces

Metaclasses can implement __prepare__ to return a specialized mapping. new_class respects this protocol, unlike a simplistic approach that builds a dictionary and calls type.

types.prepare_class

For complete control, types.prepare_class() calculates the metaclass and returns the prepared namespace. new_class combines those steps into a convenient interface.

Dynamic bases and __mro_entries__

Objects used as bases can provide __mro_entries__ and be replaced by actual classes. The types module also provides resolve_bases. This mechanism appears in generics and advanced frameworks.

Creating methods with closures

def create_model(name, fields):
    def body(ns):
        ns["__annotations__"] = dict(fields)

        def __repr__(self):
            values = ", ".join(
                f"{field}={getattr(self, field, None)!r}"
                for field in fields
            )
            return f"{name}({values})"

        ns["__repr__"] = __repr__

    return new_class(name, (), exec_body=body)

Be careful with loop-variable capture when generating multiple methods.

Setting __module__ and __qualname__

Public generated classes should have coherent metadata for documentation, error messages, and serialization.

def body(ns):
    ns["__module__"] = __name__
    ns["__doc__"] = "Dynamically generated model."

Pickle and importability

Pickle usually needs the class to be available through an importable module name. A class created inside a function may need to be registered in the module namespace.

Applying decorators

The returned class can be passed to dataclasses.dataclass, registries, or other decorators. Frameworks that inspect the class during creation may require attributes to exist inside exec_body.

Security

Do not turn untrusted schemas directly into bases, metaclasses, identifiers, or executable code. Validate names, use allowlists, and avoid exec when structured namespace construction is sufficient.

new_class versus type

type(name, bases, namespace) is concise when the namespace is already available. new_class is better when reproducing the class-definition process, including metaclass selection and prepared namespaces.

Common mistakes

  • Generating a class when a dataclass or simple object is enough.
  • Forgetting __module__ and breaking pickle or documentation.
  • Capturing loop variables incorrectly.
  • Trusting externally supplied bases or metaclasses.
  • Skipping MRO, inheritance, and introspection tests.

Centralize generation in a factory, validate schemas, choose deterministic names, preserve metadata, and test generated classes as public APIs. See the internal guides to the types module, object-oriented programming, and dataclasses.

Conclusion

types.new_class provides a structured way to generate runtime classes while respecting metaclasses, prepared namespaces, and inheritance. It is best suited to framework infrastructure driven by dynamic definitions.

External sources

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    A close-up shot showcasing the intricate scales of a snake, highlighting texture and color.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    partialmethod: Build Specialized Methods in Python

    Learn Python partialmethod for specialized methods with correct binding, fewer wrappers, clear domain names, and safe introspection.

    Ler mais

    Tempo de leitura: 2 minutos
    30/08/2026
    Close-up view of a computer screen displaying code in a software development environment.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    inspect.getmembers_static: Inspect Attributes Safely

    Use Python inspect.getmembers_static to list attributes without executing properties, descriptors, or unwanted dynamic lookup.

    Ler mais

    Tempo de leitura: 2 minutos
    30/08/2026
    Detailed close-up of yellow and white albino python scales, capturing texture and pattern.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    DynamicClassAttribute: Descriptors and Dynamic Access

    Understand Python DynamicClassAttribute, descriptors, class versus instance access, metaclasses, Enum behavior, and safe introspection.

    Ler mais

    Tempo de leitura: 2 minutos
    30/08/2026
    Close-up of vibrant JavaScript code featuring functions and syntax highlighting.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python UserList: Custom Sequences

    Learn Python UserList for custom mutable sequences with validation, normalization, mutation rules, copying, and predictable APIs.

    Ler mais

    Tempo de leitura: 2 minutos
    30/08/2026
    A laptop screen showing a code editor with visible programming code in a dimly lit environment.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python UserDict: Custom Mappings

    Learn Python UserDict for custom mappings with validation, normalization, composition, copying, and predictable mutation behavior.

    Ler mais

    Tempo de leitura: 2 minutos
    30/08/2026
    Detailed close-up of yellow and white albino python scales, capturing texture and pattern.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    itertools.accumulate: Running Sums and State

    Learn Python itertools.accumulate for running sums, balances, records, custom state transitions, and lazy data pipelines.

    Ler mais

    Tempo de leitura: 2 minutos
    30/08/2026