Python
Dynamic Programming
Object-Oriented Programming
Class Creation
Programming Tutorial

How to dynamically create a class?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Python lets you create classes at runtime, which is useful when type definitions depend on configuration, schemas, or plugin metadata that is not known until the program starts. The usual tool is the built-in type constructor, though for many real projects the harder question is not how to do it, but whether a dynamically created class is genuinely better than a normal class or a dataclass.

Create a Class With type

The three-argument form of type creates a new class object from a name, a tuple of base classes, and a dictionary of attributes.

python
1def init(self, name):
2    self.name = name
3
4
5def greet(self):
6    return f"Hello, {self.name}"
7
8
9Person = type(
10    "Person",
11    (object,),
12    {
13        "species": "human",
14        "__init__": init,
15        "greet": greet,
16    },
17)
18
19user = Person("Ava")
20print(user.greet())
21print(Person.species)

The resulting Person behaves like any other Python class. You can instantiate it, subclass it, inspect it, and call its methods normally.

Use Named Functions Instead of Large Lambdas

You can put lambdas in the attribute dictionary, but named functions are usually a better choice. They give clearer tracebacks, are easier to test, and make the class definition less cryptic.

This matters because dynamic class creation is already harder to read than a normal class statement. If the methods are also anonymous one-liners, the code becomes fragile quickly.

A good rule is:

  • keep the dynamic part focused on assembling the class
  • keep method bodies as ordinary functions
  • avoid hiding business logic inside the type(...) call itself

That keeps the design understandable even when the class name or field list is determined at runtime.

Build Classes From Runtime Configuration

A common use case is schema-driven object creation. For example, you may receive a list of required fields from a configuration file and want a lightweight model class for each schema.

python
1def make_model_class(name, fields):
2    def __init__(self, **kwargs):
3        for field in fields:
4            if field not in kwargs:
5                raise ValueError(f"Missing field: {field}")
6            setattr(self, field, kwargs[field])
7
8    def as_dict(self):
9        return {field: getattr(self, field) for field in fields}
10
11    return type(name, (), {"__init__": __init__, "as_dict": as_dict})
12
13
14User = make_model_class("User", ["id", "email"])
15user = User(id=1, email="[email protected]")
16print(user.as_dict())

This approach is useful in small frameworks, adapters, and plugin systems where the field set changes with runtime input.

Dynamic Inheritance and Mixins

You can also choose base classes dynamically. That is helpful when capabilities are optional and configured at runtime.

python
1class TimestampMixin:
2    def stamp(self):
3        return "2026-03-11"
4
5
6def describe(self):
7    return f"{self.stamp()} - {self.value}"
8
9
10Record = type(
11    "Record",
12    (TimestampMixin,),
13    {
14        "__init__": lambda self, value: setattr(self, "value", value),
15        "describe": describe,
16    },
17)
18
19record = Record("ready")
20print(record.describe())

The mechanism is simple, but the design can become confusing if too many combinations exist. Dynamic inheritance is best when the set of possible bases is small and deliberate.

Consider make_dataclass for Data-Carrying Types

If the generated class is mostly a container for fields, dataclasses.make_dataclass is often more maintainable than raw type.

python
1from dataclasses import make_dataclass
2
3Product = make_dataclass("Product", [("id", int), ("name", str), ("price", float)])
4item = Product(1, "Keyboard", 99.0)
5print(item)

This gives you an initializer, representation, and comparison behavior without manually wiring every method. It is often the better answer when the dynamic part is just the field list.

Common Pitfalls

The most common mistake is using dynamic classes when a normal class would be clearer. Runtime class creation is powerful, but it increases cognitive load and makes tooling support weaker.

Another issue is treating runtime-generated attributes as if they were validated automatically. If the class is built from external configuration, you still need to validate field names, defaults, and method behavior.

Debugging can also get harder if methods are defined inline with lambdas or if several different classes share the same helper functions without clear naming.

Finally, metaclasses are often mentioned in discussions like this, but they solve a broader class-creation problem. If you only need to create one or two runtime-defined classes, type or make_dataclass is usually enough.

Summary

  • Use type(name, bases, attrs) to create a class dynamically at runtime.
  • Keep method bodies as normal functions so the generated class stays readable.
  • Dynamic classes are useful for schema-driven and plugin-style designs.
  • Prefer make_dataclass when the class mainly holds data fields.
  • Choose dynamic class creation only when the runtime flexibility is worth the added complexity.

Course illustration
Course illustration

All Rights Reserved.