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 constant | What it matches |
|---|---|
FunctionType / LambdaType | User-defined functions and lambda |
GeneratorType | Objects from generator functions |
CoroutineType (3.5+) | Objects from async def |
AsyncGeneratorType (3.6+) | Objects from async generator functions |
MethodType | Bound methods on instances |
BuiltinFunctionType | C-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
- Metaclasses — Python’s metaclass system for controlling class creation
- The
typingmodule — type hints, Generic, NamedTuple, TypedDict - The
inspectmodule — introspecting live Python objects