Python types: Runtime Type Utilities

Published on: August 6, 2026
Reading time: 5 minutes
Code and structures representing runtime types with the Python types module

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 = 9000

It 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 accepted

When 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"  # TypeError

The proxy prevents changes through the public reference but reflects updates made to the original mapping.

source["retries"] = 5
print(public["retries"])  # 5

It 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.FunctionType

Built-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 result

Modern 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.

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 SimpleNamespace for data requiring validation.
  • Treating MappingProxyType as deep immutability.
  • Attaching methods dynamically without documenting behavior.
  • Constructing CodeType manually.
  • 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 def to 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.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    A vibrant array of colored thread spools neatly organized in rows, perfect for sewing enthusiasts.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python queue: Coordinate Threads

    Learn Python queue to coordinate threads with FIFO, priority, backpressure, task tracking, retries, and graceful shutdown.

    Ler mais

    Tempo de leitura: 4 minutos
    17/08/2026
    Extreme close-up of computer code displaying various programming terms and elements.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python struct: Pack Binary Data

    Learn Python struct to pack binary values, define endianness, reuse buffers, parse records, and validate external protocols safely.

    Ler mais

    Tempo de leitura: 4 minutos
    17/08/2026
    Detailed shot of a Jungle Carpet Python (Morelia spilota cheynei) in its natural habitat.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python tarfile: Create Safe TARs

    Learn Python tarfile to create compressed TARs, inspect members, and extract archives with filters, limits, and path traversal protection.

    Ler mais

    Tempo de leitura: 4 minutos
    17/08/2026
    Row of colorful office binders neatly arranged on a shelf, ideal for organization concepts.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python gzip: Compress .gz Files

    Learn Python gzip to read and write .gz files, produce reproducible streams, process large data, and limit external expansion.

    Ler mais

    Tempo de leitura: 4 minutos
    17/08/2026
    Neatly arranged blue office binders labeled with dates and names for organized storage.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python lzma: Compress XZ Files

    Learn Python lzma to create XZ files, process streams, select checks and filters, and enforce memory limits on external data.

    Ler mais

    Tempo de leitura: 4 minutos
    17/08/2026
    Detailed close-up texture of a snake's patterned skin showcasing natural patterns and scales.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python bz2: Compress with bzip2

    Learn Python bz2 to compress files and bytes, process data incrementally, handle concatenated streams, and limit external expansion.

    Ler mais

    Tempo de leitura: 4 minutos
    16/08/2026