pyguides

types module

The types module does two things. First, it provides utility functions for dynamically creating classes. Second, it gives names to built-in interpreter types that don’t appear as builtins — things like GeneratorType or ModuleType that you’d otherwise have to look up in C-level documentation. Both uses matter when you’re writing introspective or meta-level Python code.

Dynamic Class Creation

Sometimes you need to create a class at runtime — not instantiate one, but build the class itself. new_class() does exactly that:

import types

def body(ns):
    ns['greet'] = lambda self: f"Hello, I'm {self.name}"

Point = types.new_class('Point', (object,), exec_body=body)
p = Point()
p.name = "Alice"
p.greet()  # => "Hello, I'm Alice"

new_class(name, bases, kwds, exec_body) mirrors what happens when you write class Name(Bases):. The exec_body callback populates the class namespace — without it, you get an empty class.

prepare_class() is the lower-level version. It calculates the appropriate metaclass and returns a 3-tuple: (metaclass, namespace, kwds):

meta, ns, kwds = types.prepare_class('MyClass', (object,))
print(meta)  # <class 'type'>

get_original_bases() (Python 3.12+) is for introspecting generic classes. It returns the original type arguments before they collapse into __bases__:

from typing import TypeVar, Generic

T = TypeVar("T")
class Foo(Generic[T]): ...

class Bar(Foo[int], float): ...

Bar.__bases__           # => (Foo, float)
types.get_original_bases(Bar)  # => (Foo[int], float)

Type Constants for Built-in Types

Python’s interpreter creates many internal types that aren’t directly accessible as builtins. The types module exposes them so you can use them in isinstance() checks:

import types

def is_function(obj):
    return isinstance(obj, types.FunctionType)

is_function(lambda x: x)  # => True
is_function(is_function)  # => False (builtin)
is_function(abs)         # => False (builtin function)

Here’s a map of the most useful ones:

Type constantWhat it matches
FunctionType / LambdaTypeUser-defined functions and lambda
GeneratorTypeObjects from generator functions
CoroutineType (3.5+)Objects from async def
AsyncGeneratorType (3.6+)Objects from async generator functions
MethodTypeBound methods on instances
BuiltinFunctionTypeC-level functions like len()
NoneType (3.10+)The value None
NotImplementedType (3.10+)The value NotImplemented
EllipsisType (3.10+)The value Ellipsis
import types

async def async_func():
    return 42

types.isawaitable(async_func())              # => True
isinstance(async_func(), types.CoroutineType) # => True

ModuleType

ModuleType constructs module objects directly. The constructor takes a module name and an optional docstring:

import types

mod = types.ModuleType("my_cool_module", "Does things.")
mod.x = 10
mod.my_func = lambda: "hello"

import sys
sys.modules["my_cool_module"] = mod  # now importable as a side-effect

Modules created this way have minimal structure. importlib.util.module_from_spec() creates more complete modules with proper __spec__ set — use that when you need a fully initialized module.

SimpleNamespace

SimpleNamespace is a bare object subclass that gives you attribute-style namespace access without the overhead of a full class:

import types

ns = types.SimpleNamespace(x=1, y=2)
ns.z = 3
ns  # => namespace(x=1, y=2, z=3)

del ns.y

# Supports equality
types.SimpleNamespace(a=1) == types.SimpleNamespace(a=1)  # => True

It’s often a cleaner alternative to class _: pass when you just need a bag of attributes. It’s also useful in tests where you want to mock objects without defining a class.

You can initialize it with keyword arguments, a single mapping, or both:

types.SimpleNamespace(**{"a": 1, "b": 2})
types.SimpleNamespace({"a": 1, "b": 2})
types.SimpleNamespace({"a": 1}, b=2)

MappingProxyType

MappingProxyType wraps a dict in a read-only view. Any attempt to modify it raises TypeError:

import types

inner = {"key": "value"}
proxy = types.MappingProxyType(inner)

proxy["key"]     # => 'value'
proxy["key"] = "new"  # TypeError

# But the original dict is still mutable
inner["key"] = "changed"
proxy["key"]     # => 'changed'

It’s useful when you want to expose a dict’s contents for read-only access — a window into a dict you don’t want callers to modify.

DynamicClassAttribute

DynamicClassAttribute is a descriptor that routes attribute access through __getattr__ when accessed on the class, but behaves normally on instances:

import types

class Foo:
    @types.DynamicClassAttribute
    def virtual_attr(self):
        return "instance value"

f = Foo()
f.virtual_attr          # => 'instance value' (descriptor behavior)

# Accessed on the class — routes to __getattr__
Foo.virtual_attr        # AttributeError — __getattr__ not defined here

This is the mechanism enum.Enum uses to allow members with the same names as methods to coexist. In practice, you’ll use this more as a reader than a writer — but it’s there if you need it.

coroutine

types.coroutine() converts a generator-based coroutine function into a proper awaitable. It’s mostly a compatibility tool for code written before async def existed:

import types

def legacy_generator():
    yield 1
    yield 2

coro = types.coroutine(legacy_generator)
import inspect
inspect.iscoroutine(coro)  # => True (it's now awaitable)

If you pass it a regular function, it wraps the return value. If the return value is already a generator, it wraps it in an awaitable proxy.

See Also