Skip to main content

Python Dynamic Class Creation Practice Problems & Exercises

Practice: Dynamic Class Creation

11 problems3 Easy4 Medium4 Hard70–90 min
← Back to lesson

Easy

#1Build a Class with type() Three-Argument FormEasy
typedynamic-classnamespace

Create a Rectangle class entirely at runtime using the three-argument form of type(). It must have __init__, __repr__, and an area() method.

Python
def rect_init(self, width, height):
    self.width = width
    self.height = height

def rect_repr(self):
    return f"Rectangle(width={self.width}, height={self.height})"

def rect_area(self):
    return self.width * self.height

Rectangle = type(
    "Rectangle",
    (object,),
    {
        "__init__": rect_init,
        "__repr__": rect_repr,
        "area": rect_area,
    },
)

r = Rectangle(4, 5)
print(r)
print(f"area = {r.area()}")
Expected Output
Rectangle(width=4, height=5)
area = 20
Hints

Hint 1: type(name, bases, dict) creates a class named name, inheriting from bases, with the given dict as its namespace.

Hint 2: Pass __init__, __repr__, and any methods as regular functions in the dict.


#2Dynamic Inheritance with type()Easy
typeinheritancedynamic-classbases

Use type() to create an AdminUser class that dynamically inherits from both User and Auditable.

Python
class User:
    def __init__(self, username):
        self.username = username

    def greet(self):
        return f"Hello, {self.username}"


class Auditable:
    def audit_log(self):
        return f"Audit: {self.__class__.__name__} accessed"


def admin_init(self, username):
    User.__init__(self, username)
    self.permissions = ["read", "write", "delete"]

AdminUser = type(
    "AdminUser",
    (User, Auditable),   # Dynamic multi-inheritance
    {
        "__init__": admin_init,
    },
)

admin = AdminUser("alice")
print(f"AdminUser is subclass of User: {issubclass(AdminUser, User)}")
print(f"AdminUser is subclass of Auditable: {issubclass(AdminUser, Auditable)}")
print(f"AdminUser permissions: {admin.permissions}")
Expected Output
AdminUser is subclass of User: True
AdminUser is subclass of Auditable: True
AdminUser permissions: ['read', 'write', 'delete']
Hints

Hint 1: Pass a tuple of base classes as the second argument to type(). The new class inherits from all of them.

Hint 2: The namespace dict overrides or extends anything defined on the parents.


#3Dynamically Add Methods to an Existing ClassEasy
dynamic-classmonkey-patchingsetattr

Start with a minimal Robot class, then dynamically attach two new methods (greet and describe) to it at runtime using setattr.

Python
class Robot:
    def __init__(self, name, version):
        self.name = name
        self.version = version


# Dynamically add methods
def greet(self):
    return f"Hello, I am a {self.name}"

def describe(self):
    return f"{self.name} (v{self.version})"

setattr(Robot, "greet", greet)
setattr(Robot, "describe", describe)

r = Robot("Robot", "2.0")
print(f"greet() added: {r.greet()}")
print(f"describe() added: {r.describe()}")
Expected Output
greet() added: Hello, I am a Robot
describe() added: Robot (v2.0)
Hints

Hint 1: setattr(MyClass, method_name, function) adds or replaces a method at runtime.

Hint 2: The first parameter of the function will receive the instance (self) when called as a method.


Medium

#4Struct Factory — Generate Classes from Field ListsMedium
class-factorystructtypedynamic

Write a make_struct factory that generates a named struct class from a list of field names, similar to a simplified namedtuple.

Python
def make_struct(name, fields):
    fields = tuple(fields)

    def struct_init(self, *args, **kwargs):
        if args:
            if len(args) != len(fields):
                raise TypeError(
                    f"{name} takes {len(fields)} arguments, got {len(args)}"
                )
            for f, v in zip(fields, args):
                setattr(self, f, v)
        else:
            for f in fields:
                setattr(self, f, kwargs[f])

    def struct_repr(self):
        parts = [f"{f}={getattr(self, f)!r}" for f in fields]
        return f"{name}(" + ", ".join(parts) + ")"

    def struct_eq(self, other):
        if type(self) is not type(other):
            return NotImplemented
        return all(getattr(self, f) == getattr(other, f) for f in fields)

    return type(
        name,
        (object,),
        {
            "__init__": struct_init,
            "__repr__": struct_repr,
            "__eq__": struct_eq,
            "_fields": fields,
        },
    )


Point = make_struct("Point", ["x", "y"])
Color = make_struct("Color", ["r", "g", "b"])

p = Point(1, 2)
c = Color(255, 128, 0)

print(p)
print(c)
print(f"Point fields: {Point._fields}")
print(f"Color fields: {Color._fields}")
Expected Output
Point(x=1, y=2)
Color(r=255, g=128, b=0)
Point fields: ('x', 'y')
Color fields: ('r', 'g', 'b')
Hints

Hint 1: Write a function make_struct(name, fields) that builds a class with __init__, __repr__, and a _fields class attribute.

Hint 2: Build __init__ by dynamically assigning each field from *args or **kwargs.


#5Enum-Like Class FactoryMedium
class-factoryenumtypedynamic

Build a make_enum factory that creates an enum-like class from a list of member names, with integer values starting from 0. Support lookup by value and by name.

Python
def make_enum(name, members):
    namespace = {}
    value_to_name = {}

    for idx, member in enumerate(members):
        namespace[member] = idx
        value_to_name[idx] = member

    namespace["_value_to_name"] = value_to_name
    namespace["_members"] = {m: i for i, m in enumerate(members)}

    @classmethod
    def by_value(cls, val):
        return cls._value_to_name[val]

    @classmethod
    def from_name(cls, n):
        return cls._members[n]

    namespace["__getitem__"] = by_value.__func__  # class[val]
    namespace["by_value"] = by_value
    namespace["from_name"] = from_name

    cls = type(name, (object,), namespace)
    # Make Status[1] work via class-level __getitem__
    # We need to attach it on the metaclass level — use a simple workaround:
    cls.__class_getitem__ = classmethod(lambda c, val: c._value_to_name[val])
    return cls


Status = make_enum("Status", ["PENDING", "ACTIVE", "CLOSED"])

print(f"Status.PENDING = {Status.PENDING}")
print(f"Status.ACTIVE = {Status.ACTIVE}")
print(f"Status.CLOSED = {Status.CLOSED}")
print(f"Status[1] = {Status.by_value(1)}")
print(f"Status('ACTIVE') = {Status.from_name('ACTIVE')}")
Expected Output
Status.PENDING = 0
Status.ACTIVE = 1
Status.CLOSED = 2
Status[1] = ACTIVE
Status('ACTIVE') = 1
Hints

Hint 1: Create the class namespace with each member name as a key and its integer index as the value.

Hint 2: Add a __getitem__ classmethod (or a class-level dict _by_value) to support lookup by integer. Add __call__ support by adding __call__ or __new__.


#6Mixin Injector — Runtime Mixin CompositionMedium
mixintypedynamic-classcomposition

Write an inject_mixins function that takes a base class and any number of mixin classes and returns a new dynamically-created class that inherits from all of them.

Python
def inject_mixins(cls, *mixins):
    """Return a new class that inherits from cls and all mixins."""
    name = cls.__name__ + "Extended"
    return type(name, (cls, *mixins), {})


class Service:
    def __init__(self, name):
        self.name = name

    def serialize(self):
        return f"Service({self.name!r})"


class JsonMixin:
    def serialize(self):
        import json
        return json.dumps({"name": self.name, "age": 30})


class LogMixin:
    def log_call(self, method_name):
        print(f"[LOG] {self.__class__.__name__}.{method_name} called")


class CacheMixin:
    _cache = {}

    def cached_serialize(self):
        key = self.name
        if key not in self._cache:
            self._cache[key] = self.serialize()
            return self._cache[key]
        return "cache hit"


LoggedService = inject_mixins(Service, LogMixin)
JsonService = inject_mixins(Service, JsonMixin)
CachedJsonService = inject_mixins(Service, JsonMixin, CacheMixin)

ls = LoggedService("svc")
print(f"LoggedService methods: {[m for m in dir(ls) if not m.startswith('_') and callable(getattr(ls, m))][:2]}")

js = JsonService("Alice")
print(f"JsonMixin method works: {js.serialize()}")

cjs = CachedJsonService("Alice")
_ = cjs.cached_serialize()  # fills cache
print(f"CachedMixin method works: {cjs.cached_serialize()}")
Expected Output
LoggedService methods: serialize, log_call
JsonMixin method works: {"name": "Alice", "age": 30}
CachedMixin method works: cache hit
Hints

Hint 1: Create a function inject_mixins(cls, *mixins) that builds a new class via type() with (cls, *mixins) as the bases.

Hint 2: The new class name can be something like cls.__name__ + "Extended".


#7Class Builder with Validation PipelineMedium
class-factoryvalidationpipelinetype

Write a ClassBuilder that provides a fluent interface for declaring fields with validators, then generates a class dynamically.

Python
class ClassBuilder:
    def __init__(self, name):
        self.name = name
        self._fields = []  # list of (field_name, field_type, validator_or_None)

    def field(self, name, field_type, validator=None):
        self._fields.append((name, field_type, validator))
        return self   # fluent

    def build(self):
        fields = list(self._fields)
        class_name = self.name

        def generated_init(self_inner, *args, **kwargs):
            if args:
                pairs = zip(fields, args)
            else:
                pairs = ((f, kwargs[f[0]]) for f in fields)

            for (fname, ftype, fvalidator), value in pairs:
                if not isinstance(value, ftype):
                    try:
                        value = ftype(value)
                    except Exception:
                        raise TypeError(f"{fname} must be {ftype.__name__}")
                if fvalidator:
                    fvalidator(fname, value)
                setattr(self_inner, fname, value)

        def generated_repr(self_inner):
            parts = [f"{f[0]}={getattr(self_inner, f[0])!r}" for f in fields]
            return f"{class_name}(" + ", ".join(parts) + ")"

        return type(
            class_name,
            (object,),
            {
                "__init__": generated_init,
                "__repr__": generated_repr,
                "_fields_meta": fields,
            },
        )


def positive(name, value):
    if value <= 0:
        raise ValueError(f"{name} must be > 0, got {value}")


Order = (
    ClassBuilder("Order")
    .field("item", str)
    .field("qty", int, positive)
    .field("price", float, positive)
    .build()
)

o = Order("Widget", 5, 9.99)
print(f"{o} total={o.qty * o.price:.2f}")

try:
    Order("Widget", 0, 9.99)
except ValueError as e:
    print(f"ValueError: {e}")

try:
    Order("Widget", 3, -1.0)
except ValueError as e:
    print(f"ValueError: {e}")
Expected Output
Order(item='Widget', qty=5, price=9.99) total=49.95
ValueError: qty must be > 0, got 0
ValueError: price must be > 0, got -1.0
Hints

Hint 1: Build a ClassBuilder that accumulates field definitions (name, type, validator) and emits the class when .build() is called.

Hint 2: Generate __init__ that runs each validator against the corresponding argument.


Hard

#8Dataclass-Style Generator Without @dataclassHard
class-factorydataclasstype__init____repr____eq__

Build a dataclass_like function that inspects a class body's __annotations__ and injects __init__, __repr__, __eq__, and __hash__. Add a frozen flag that prevents attribute mutation.

Python
class FrozenInstanceError(AttributeError):
    pass


def dataclass_like(cls=None, frozen=False):
    def decorator(klass):
        annotations = klass.__dict__.get("__annotations__", {})
        fields = list(annotations.keys())
        defaults = {
            f: klass.__dict__[f]
            for f in fields
            if f in klass.__dict__
        }

        # Build __init__
        def generated_init(self, *args, **kwargs):
            bound = {}
            for i, fname in enumerate(fields):
                if i < len(args):
                    bound[fname] = args[i]
                elif fname in kwargs:
                    bound[fname] = kwargs[fname]
                elif fname in defaults:
                    bound[fname] = defaults[fname]
                else:
                    raise TypeError(f"Missing field: {fname}")
            for fname, value in bound.items():
                object.__setattr__(self, fname, value)

        def generated_repr(self):
            parts = [f"{f}={getattr(self, f)!r}" for f in fields]
            return f"{klass.__name__}(" + ", ".join(parts) + ")"

        def generated_eq(self, other):
            if type(self) is not type(other):
                return NotImplemented
            return all(getattr(self, f) == getattr(other, f) for f in fields)

        def generated_hash(self):
            return hash(tuple(getattr(self, f) for f in fields))

        new_ns = dict(klass.__dict__)
        new_ns["__init__"] = generated_init
        new_ns["__repr__"] = generated_repr
        new_ns["__eq__"] = generated_eq
        if frozen:
            new_ns["__hash__"] = generated_hash

            def frozen_setattr(self, name, value):
                if name in fields:
                    raise FrozenInstanceError(
                        f"cannot assign to field '{name}'"
                    )
                object.__setattr__(self, name, value)

            new_ns["__setattr__"] = frozen_setattr

        return type(klass.__name__, klass.__bases__, new_ns)

    if cls is not None:
        return decorator(cls)
    return decorator


@dataclass_like(frozen=True)
class Point3D:
    x: float
    y: float
    z: float


p1 = Point3D(1.0, 2.0, 3.0)
p2 = Point3D(1.0, 2.0, 3.0)
print(p1)
print(f"Equal points: {p1 == p2}")
print(f"Hash works: {hash(p1) == hash(p2)}")

try:
    p1.x = 99.0
except FrozenInstanceError as e:
    print(f"Frozen violation: FrozenInstanceError: {e}")
Expected Output
Point3D(x=1.0, y=2.0, z=3.0)
Equal points: True
Hash works: True
Frozen violation: FrozenInstanceError: cannot assign to field 'x'
Hints

Hint 1: Inspect the class __annotations__ dict to extract field names and optional default values.

Hint 2: Generate __init__ from annotations, __repr__ from field names, __eq__ by comparing all field values, and optionally __hash__ when frozen=True.

Hint 3: For frozen, generate __setattr__ and __delattr__ that raise FrozenInstanceError.


#9Runtime Class Patching with Version ControlHard
dynamic-classpatchingversioningtype

Build a runtime class patcher that supports versioned method replacement and rollback. Each patch() saves the prior state; rollback() restores it.

Python
class ClassPatcher:
    _history = {}   # cls -> list of (method_name, old_method)

    @classmethod
    def patch(cls, target_cls, method_name, new_func):
        if target_cls not in cls._history:
            cls._history[target_cls] = []
        old = getattr(target_cls, method_name, None)
        cls._history[target_cls].append((method_name, old))
        setattr(target_cls, method_name, new_func)

    @classmethod
    def rollback(cls, target_cls):
        if not cls._history.get(target_cls):
            raise RuntimeError("Nothing to rollback")
        method_name, old = cls._history[target_cls].pop()
        if old is None:
            delattr(target_cls, method_name)
        else:
            setattr(target_cls, method_name, old)


class Greeter:
    def say_hello(self):
        return "hello from original"


g = Greeter()
print(f"v1: {g.say_hello()}")

def say_hello_v2(self):
    return "hello from patched (double)"

ClassPatcher.patch(Greeter, "say_hello", say_hello_v2)
print(f"v2: {g.say_hello()}")

def say_hello_v3(self):
    result = "hello from patched (double) with logging"
    print(f"[LOG] say_hello called")
    return result

ClassPatcher.patch(Greeter, "say_hello", say_hello_v3)
print(f"v3: {g.say_hello()}")

# Roll back twice
ClassPatcher.rollback(Greeter)
ClassPatcher.rollback(Greeter)
print(f"Rolled back to v1: {g.say_hello()}")
Expected Output
v1: hello from original
v2: hello from patched (double)
v3: hello from patched (double) with logging
Rolled back to v1: hello from original
Hints

Hint 1: Maintain a stack of (method_name, old_method) tuples on the class to support rollback.

Hint 2: patch(cls, method_name, new_func) saves the old method, applies the new one, and records the change. rollback(cls) pops and restores the previous method.


#10Interface Enforcer via Dynamic Class FactoryHard
class-factoryinterfaceABCtype

Build a make_interface factory that creates an interface class. Any subclass is checked at definition time to ensure it implements all required methods.

Python
class InterfaceError(TypeError):
    pass


def make_interface(name, required_methods):
    required = frozenset(required_methods)

    def interface_init_subclass(cls, **kwargs):
        super_cls = interface_base
        type.__init_subclass__.__func__(super_cls)
        missing = required - set(cls.__dict__)
        if missing:
            raise InterfaceError(
                f"{cls.__name__} missing required methods: {missing}"
            )

    interface_base = type(
        name,
        (object,),
        {
            "__init_subclass__": classmethod(
                lambda cls, **kw: (
                    setattr(cls, "_implements_" + name, True),
                    (_ for _ in ()).throw(
                        InterfaceError(
                            f"{cls.__name__} missing required methods: "
                            f"{required - set(cls.__dict__)}"
                        )
                    ) if (required - set(cls.__dict__)) else None,
                )
            ),
        },
    )

    # Cleaner approach: override __init_subclass__ directly
    def clean_init_subclass(cls, **kwargs):
        missing = required - set(cls.__dict__)
        if missing:
            raise InterfaceError(
                f"{cls.__name__} missing required methods: {missing}"
            )

    interface_base.__init_subclass__ = classmethod(clean_init_subclass)
    return interface_base


Serializable = make_interface("Serializable", ["serialize", "deserialize"])


class JSONSerializer(Serializable):
    def serialize(self, data):
        import json
        return json.dumps(data)

    def deserialize(self, text):
        import json
        return json.loads(text)


print(f"JSONSerializer implements interface: {hasattr(JSONSerializer, 'serialize')}")

try:
    class XMLSerializer(Serializable):
        def deserialize(self, text):
            return text
except InterfaceError as e:
    print(f"InterfaceError: {e}")
Expected Output
JSONSerializer implements interface: True
InterfaceError: XMLSerializer missing required methods: {'serialize'}
Hints

Hint 1: Write make_interface(name, required_methods) that returns an interface class with a custom __init_subclass__ verifying required methods.

Hint 2: Alternatively, build a validates_interface(cls, interface) checker that walks cls.__dict__ and checks for each required method name.


#11Hierarchical Class Factory with Shared MetaclassHard
class-factorymetaclasshierarchyregistry

Build a make_typed_node factory that generates typed node classes in a registry. Each node validates its value type. The base class tracks all generated node types.

Python
class NodeRegistry:
    _nodes = {}

    def __init_subclass__(cls, value_type=None, **kwargs):
        super().__init_subclass__(**kwargs)
        if value_type is not None:
            cls.value_type = value_type
            NodeRegistry._nodes[value_type.__name__] = cls


class Node(NodeRegistry):
    value_type = object

    def __init__(self, value):
        if not isinstance(value, self.value_type):
            # Try coercion
            try:
                value = self.value_type(value)
            except (TypeError, ValueError) as exc:
                raise TypeError(
                    f"{type(self).__name__}: cannot coerce {value!r} to "
                    f"{self.value_type.__name__}: {exc}"
                )
        self.value = value

    def __repr__(self):
        return f"{type(self).__name__}({self.value!r})"


def make_typed_node(value_type):
    class_name = value_type.__name__.capitalize() + "Node"
    return type(
        class_name,
        (Node,),
        {"value_type": value_type, "__init_subclass_kw__": {"value_type": value_type}},
    )


# Register via __init_subclass__ keyword
class IntNode(Node, value_type=int):
    pass

class StrNode(Node, value_type=str):
    pass

class FloatNode(Node, value_type=float):
    pass


print(f"Node classes: {list(NodeRegistry._nodes.keys())}")

n1 = IntNode(42)
n2 = StrNode("hello")
n3 = FloatNode(3)  # coercion from int

print(f"IntNode(42) valid: {isinstance(n1.value, int)}")
print(f"StrNode('hello') valid: {isinstance(n2.value, str)}")
print(f"FloatNode(3.14) coerced from int: {FloatNode(3.0).value}")
Expected Output
Node classes: ['IntNode', 'StrNode', 'FloatNode']
IntNode(42) valid: True
StrNode('hello') valid: True
FloatNode(3.14) coerced from int: 3.0
Hints

Hint 1: Write make_typed_node(value_type) that returns a class subclassing a base Node, with __init__ validating and storing the value.

Hint 2: Use a shared metaclass or __init_subclass__ on Node to register every generated class by value_type name.

© 2026 EngineersOfAI. All rights reserved.