Functions, methods, modules, generators, frames, code objects, and generic aliases are real runtime objects in Python. The Python types module provides standard names for many of these structures and utilities for dynamic class creation, namespace preparation, generator-to-coroutine adaptation, and read-only mapping views.
This guide covers SimpleNamespace, MappingProxyType, MethodType, new_class(), prepare_class(), resolve_bases(), and the runtime types exposed by the module. It complements our articles about Python inspect, bytecode with dis, symtable, contextvars, and copyreg.
Why the types module exists
Many internal types can be discovered with type(), but expressions such as type(lambda: None) are unclear in production code. The module provides explicit names such as FunctionType, GeneratorType, CoroutineType, and TracebackType.
import types
print(isinstance(lambda: None, types.FunctionType))
print(isinstance((x for x in range(3)), types.GeneratorType))These names help introspection, debuggers, frameworks, and low-level validation. Business logic should often prefer protocols and collections.abc when behavior matters more than the exact implementation type.
SimpleNamespace
SimpleNamespace creates a small object whose attributes are stored in an internal dictionary.
from types import SimpleNamespace
config = SimpleNamespace(
host="localhost",
port=8000,
debug=True,
)
print(config.host)
config.port = 9000It is convenient for small results, temporary configuration, mocks, and grouping values. Its representation displays attributes, and equality compares namespace contents.
SimpleNamespace is not a domain model
The object validates no fields, imposes no types, and freely accepts new attributes.
config.prt = 7000 # typo is acceptedWhen invariants, documentation, methods, or validation matter, choose a dataclass, NamedTuple, TypedDict, or ordinary class.
MappingProxyType
MappingProxyType creates a dynamic read-only view of a mapping.
from types import MappingProxyType
source = {"mode": "production", "retries": 3}
public = MappingProxyType(source)
print(public["mode"])
# public["mode"] = "test" # TypeErrorThe proxy prevents changes through the public reference but reflects updates made to the original mapping.
source["retries"] = 5
print(public["retries"]) # 5It provides read-only access, not deep immutability.
When to use MappingProxyType
It is useful for exposing registries, metadata, internal configuration, and dispatch tables without returning a mutable mapping.
_handlers = {"json": process_json}
handlers = MappingProxyType(_handlers)Nested values can still be mutable. If the mapping contains lists, callers may mutate those lists. Copy or freeze nested values when necessary.
MethodType
MethodType binds a function to an instance, producing a bound method.
from types import MethodType
class User:
def __init__(self, name):
self.name = name
def greet(self):
return f"Hello, {self.name}"
user = User("Ana")
user.greet = MethodType(greet, user)
print(user.greet())This appears in tests, instrumentation, and plugin systems. Permanent behavior is usually clearer through composition or subclasses.
FunctionType and LambdaType
FunctionType represents Python-defined functions. LambdaType is an alias for the same type.
import types
def add(a, b):
return a + b
assert isinstance(add, types.FunctionType)
assert types.LambdaType is types.FunctionTypeBuilt-ins such as len use BuiltinFunctionType, while built-in methods may use BuiltinMethodType.
Generator, coroutine, and async-generator types
import types
def numbers():
yield 1
async def fetch():
return 42
async def events():
yield "start"
generator = numbers()
coroutine = fetch()
async_generator = events()
print(isinstance(generator, types.GeneratorType))
print(isinstance(coroutine, types.CoroutineType))
print(isinstance(async_generator, types.AsyncGeneratorType))Created coroutines must be awaited or closed to avoid warnings.
types.coroutine()
types.coroutine() turns a generator function into an await-compatible coroutine. It mainly supports low-level interoperability between generator-based and native async systems.
import types
@types.coroutine
def wait_for_event():
result = yield "event"
return resultModern applications should normally use async def. The decorator remains useful to runtime libraries and legacy adapters.
ModuleType
ModuleType creates module objects.
from types import ModuleType
module = ModuleType("my_module", "Dynamically created module")
module.value = 42
print(module.__name__)
print(module.value)For modules that participate in imports, the official importlib documentation recommends specs and importlib.util.module_from_spec(), which initializes more attributes correctly.
Create classes with new_class()
new_class() implements the modern dynamic-class creation protocol.
import types
def fill(namespace):
namespace["category"] = "dynamic"
def describe(self):
return self.category
namespace["describe"] = describe
Dynamic = types.new_class(
"Dynamic",
bases=(object,),
exec_body=fill,
)
print(Dynamic().describe())The helper respects metaclasses, __prepare__(), and base resolution instead of requiring applications to reproduce that protocol manually.
new_class() parameters
The first argument is the class name; bases contains base classes; kwds can include metaclass; and exec_body receives the prepared namespace.
Plugin = types.new_class(
"Plugin",
bases=(BasePlugin,),
kwds={"metaclass": PluginMeta},
exec_body=lambda ns: ns.update({"version": 1}),
)Validate names, bases, and metaclasses that come from configuration. Dynamic class creation is not a sandbox.
prepare_class()
prepare_class() calculates the appropriate metaclass and prepares a namespace before class creation.
meta, namespace, keywords = types.prepare_class(
"MyClass",
(Base,),
{"metaclass": MyMeta},
)
namespace["attribute"] = 10
MyClass = meta("MyClass", (Base,), namespace, **keywords)Frameworks can inspect or populate the namespace between preparation and construction.
resolve_bases()
Class bases may include objects implementing __mro_entries__() instead of real types. resolve_bases() applies this protocol.
resolved = types.resolve_bases(original_bases)This matters for generic aliases and abstractions used in a class’s base list. Use resolved bases when invoking a metaclass directly.
get_original_bases()
get_original_bases() retrieves bases declared before resolution when metadata was preserved in __orig_bases__.
from typing import Generic, TypeVar
T = TypeVar("T")
class Box(Generic[T]):
pass
class TextBox(Box[str]):
pass
print(types.get_original_bases(TextBox))Typing frameworks can use this to recover generic parameters. Missing metadata should be treated as a normal case.
DynamicClassAttribute
DynamicClassAttribute creates a descriptor similar to property but permits different handling during class-level access.
It is used by implementations such as enum. Ordinary application code should usually prefer property.
CodeType
CodeType represents compiled code objects.
code = compile("x = 1", "<example>", "exec")
print(isinstance(code, types.CodeType))The CodeType constructor changes across Python versions and exposes many interpreter details. Prefer compile() and code.replace().
new_code = code.replace(co_filename="virtual_file.py")Executing a code object remains code execution and should never use unknown input.
FrameType and TracebackType
FrameType represents execution frames; TracebackType represents an exception’s traceback chain.
try:
1 / 0
except ZeroDivisionError as error:
tb = error.__traceback__
print(isinstance(tb, types.TracebackType))
print(isinstance(tb.tb_frame, types.FrameType))Frames retain references to local values and can extend object lifetimes. Release references after diagnostics.
GenericAlias and UnionType
GenericAlias represents expressions such as list[int]. UnionType represents unions produced with |.
alias = list[int]
union = int | str
print(isinstance(alias, types.GenericAlias))
print(isinstance(union, types.UnionType))These objects support annotation introspection but do not validate runtime values.
Singleton-related types
The module provides names such as NoneType, EllipsisType, and NotImplementedType.
print(isinstance(None, types.NoneType))
print(isinstance(Ellipsis, types.EllipsisType))
print(isinstance(NotImplemented, types.NotImplementedType))This avoids expressions such as type(None) in introspection APIs.
Extension descriptors
GetSetDescriptorType and MemberDescriptorType represent descriptors commonly created by C extensions and __slots__.
class Example:
__slots__ = ("value",)
print(isinstance(Example.value, types.MemberDescriptorType))Some runtimes may use the same internal type for both descriptor categories. Test supported implementations before relying on the distinction.
Security and introspection
The module exposes powerful runtime structures. Creating classes, modules, methods, metaclasses, or code objects does not validate their origin.
Never execute functions, metaclasses, bases, or bytecode supplied by users. Plugin systems need explicit interfaces, authorization, and isolation.
Common mistakes
- Using
SimpleNamespacefor data requiring validation. - Treating
MappingProxyTypeas deep immutability. - Attaching methods dynamically without documenting behavior.
- Constructing
CodeTypemanually. - Accepting untrusted metaclasses or bases.
- Assuming annotations validate values.
- Keeping frames and tracebacks indefinitely.
- Using exact type checks when a protocol is better.
Best practices
- Use types names for clear introspection.
- Prefer dataclasses for structured models.
- Expose read-only proxies for public mappings.
- Create dynamic classes with
new_class(). - Use
module_from_spec()for imports. - Prefer
async defto legacy adapters. - Release frames after analysis.
- Test compatibility across Python versions.
Conclusion
The Python types module gives explicit names to runtime structures and utilities for namespaces, read-only mappings, bound methods, and dynamic classes. It is especially useful in frameworks, debuggers, and introspection tools.
These APIs operate close to language internals. By preferring simpler abstractions when possible, validating plugins and metaclasses, and avoiding unstable constructors, developers can use runtime flexibility without making applications fragile.





