types.FunctionType, types.CodeType, types.SimpleNamespace, and dynamic type creation with types.new_class.
The types module exposes names for many runtime object types used internally by CPython. It gives Python code a stable way to ask, “What kind of runtime object is this?” without hard-coding obscure expressions such as type(lambda: None) or type((x for x in [])).
The module is small, but it sits close to the object model. It names function objects, method objects, module objects, frame objects, code objects, generator objects, coroutine objects, mapping proxy objects, descriptors, namespace objects, and several helper classes used by the runtime.
56.1 The Role of types
types is a catalog of runtime object classes.
Example:
import types
def f():
pass
print(isinstance(f, types.FunctionType))
print(isinstance(f.__code__, types.CodeType))Output:
True
TrueConceptually:
runtime object
↓
type(obj)
↓
named runtime type from typesThe module is useful when writing tools that need to distinguish between Python-level object categories:
debuggers
profilers
serializers
documentation generators
import tools
decorator frameworks
plugin loaders
mocking libraries
runtime validators56.2 types vs type
The built-in type() function returns the concrete type of an object.
def f():
pass
print(type(f))Output shape:
<class 'function'>The types module gives a named reference to that same type:
import types
print(type(f) is types.FunctionType)Output:
TrueSo this:
type(f) is type(lambda: None)can be written more clearly as:
type(f) is types.FunctionTypeMost code should prefer isinstance() over exact type checks unless exact runtime layout matters.
56.3 Function Types
A Python function object has type types.FunctionType.
import types
def add(a, b):
return a + b
print(isinstance(add, types.FunctionType))A function object wraps:
| Field | Meaning |
|---|---|
__code__ | Code object |
__globals__ | Global namespace |
__defaults__ | Positional defaults |
__kwdefaults__ | Keyword-only defaults |
__closure__ | Closure cells |
__annotations__ | Annotations |
__dict__ | Custom attributes |
__name__ | Function name |
__qualname__ | Qualified name |
Internally, this corresponds to CPython’s function object implementation.
Conceptually:
function object
code object
globals
defaults
closuretypes.FunctionType can also construct a new function from a code object.
import types
def template():
return answer
code = template.__code__
namespace = {"answer": 42}
fn = types.FunctionType(code, namespace)
print(fn())Output:
42This shows that a function combines compiled code with a global namespace.
56.4 Code Objects
Code objects have type types.CodeType.
import types
def f(x):
return x + 1
print(isinstance(f.__code__, types.CodeType))A code object is immutable compiled program data.
It contains:
bytecode
constants
names
local variable names
free variable names
cell variable names
source filename
line information
exception table
stack size
flagsCode objects do not store runtime local values. They describe executable code.
code = f.__code__
print(code.co_name)
print(code.co_varnames)
print(code.co_consts)
print(code.co_names)A function object points to a code object. A frame executes a code object. The dis module decodes a code object.
source code
↓
compiler
↓
code object
↓
function object
↓
frame execution56.5 Creating Modified Code Objects
Code objects are immutable, but modern Python provides code.replace().
def f():
return 1
code = f.__code__
new_code = code.replace(co_name="renamed")
print(new_code.co_name)Output:
renamedThis is safer than manually calling types.CodeType(...), because the constructor signature changes across Python versions.
Use cases include:
debugging tools
coverage tools
bytecode experiments
code generation systems
function wrappers
educational bytecode workFor production code, code object mutation should be treated as CPython-version-sensitive.
56.6 Method Types
Bound Python methods have type types.MethodType.
import types
class User:
def name(self):
return "anonymous"
u = User()
print(isinstance(u.name, types.MethodType))A bound method packages:
function
instanceThat is:
print(u.name.__func__)
print(u.name.__self__)Conceptually:
obj.method
↓
descriptor lookup
↓
bound method(function, obj)Calling:
u.name()is equivalent in effect to:
User.name(u)types.MethodType can also bind a function to an object manually:
import types
class User:
pass
def greet(self):
return "hello"
u = User()
u.greet = types.MethodType(greet, u)
print(u.greet())Output:
hello56.7 Built-in Function and Method Types
C-level built-ins have separate runtime types.
import types
print(isinstance(len, types.BuiltinFunctionType))
print(isinstance([].append, types.BuiltinMethodType))Built-in functions usually have no Python code object:
print(hasattr(len, "__code__"))Output:
FalseThey are implemented in C and exposed as callable Python objects.
Conceptually:
Python call
↓
built-in callable object
↓
C function pointerThis distinction matters for tools. A debugger or signature extractor cannot inspect a built-in the same way it inspects a Python function.
56.8 Module Type
Modules have type types.ModuleType.
import types
import math
print(isinstance(math, types.ModuleType))You can create a module object directly:
import types
mod = types.ModuleType("demo")
mod.answer = 42
print(mod.answer)A module object is mostly a namespace with metadata:
| Attribute | Meaning |
|---|---|
__name__ | Module name |
__dict__ | Module namespace |
__spec__ | Import specification |
__loader__ | Loader object |
__package__ | Package name |
__file__ | Source or binary file path, when present |
sys.modules stores module objects by name.
sys.modules["math"] → module object56.9 Frame Type
Frame objects have type types.FrameType.
import sys
import types
frame = sys._getframe()
print(isinstance(frame, types.FrameType))A frame represents active or suspended execution.
It exposes:
| Attribute | Meaning |
|---|---|
f_code | Code object being executed |
f_locals | Local variables |
f_globals | Global variables |
f_builtins | Builtins |
f_back | Caller frame |
f_lineno | Current line |
f_trace | Trace function |
Frames are used by:
tracebacks
debuggers
profilers
inspect.currentframe()
sys._getframe()
generators
coroutinesHolding a frame reference can keep local variables and caller frames alive. This makes frame objects important in memory debugging.
56.10 Traceback Type
Tracebacks have type types.TracebackType.
import types
try:
1 / 0
except Exception as exc:
tb = exc.__traceback__
print(isinstance(tb, types.TracebackType))A traceback is a linked list of execution records.
Each node contains:
| Attribute | Meaning |
|---|---|
tb_frame | Frame at this level |
tb_lineno | Line number |
tb_lasti | Bytecode offset |
tb_next | Next traceback node |
Conceptually:
exception
↓
traceback node
↓
traceback node
↓
...Tracebacks retain frames. Frames retain locals. Saved exceptions can therefore retain large object graphs.
56.11 Generator Type
Generator objects have type types.GeneratorType.
import types
def gen():
yield 1
g = gen()
print(isinstance(g, types.GeneratorType))A generator object contains:
code object
suspended frame
running flag
delegated iteratorImportant attributes include:
| Attribute | Meaning |
|---|---|
gi_code | Code object |
gi_frame | Suspended frame |
gi_running | Running state |
gi_yieldfrom | Current delegated iterator |
A generator is a resumable execution frame.
call generator function
↓
create generator object
↓
start execution on next()
↓
pause at yield
↓
resume later56.12 Coroutine Type
Native coroutine objects have type types.CoroutineType.
import types
async def fetch():
return 1
coro = fetch()
print(isinstance(coro, types.CoroutineType))
coro.close()A coroutine is similar to a generator, but it participates in the await protocol.
Important attributes include:
| Attribute | Meaning |
|---|---|
cr_code | Code object |
cr_frame | Suspended frame |
cr_running | Running state |
cr_await | Object currently awaited |
Coroutine objects are created by calling an async def function.
They do not execute immediately. They execute when awaited or driven by an event loop.
56.13 Async Generator Type
Async generators have type types.AsyncGeneratorType.
import types
async def agen():
yield 1
obj = agen()
print(isinstance(obj, types.AsyncGeneratorType))Async generators combine resumable execution with asynchronous iteration.
They are consumed with:
async for item in agen():
...At the object model level, they have their own type because their protocol differs from ordinary generators and coroutines.
56.14 Cell Type
Closure cells have type types.CellType.
import types
def outer(x):
def inner():
return x
return inner
fn = outer(10)
cell = fn.__closure__[0]
print(isinstance(cell, types.CellType))
print(cell.cell_contents)A cell stores a variable captured by nested functions.
Conceptually:
outer local variable
↓
cell object
↓
inner function closureThis indirection lets a variable outlive the stack frame that originally created it.
56.15 Mapping Proxy Type
Class dictionaries are exposed as read-only mapping proxies.
import types
class User:
kind = "user"
print(type(User.__dict__) is types.MappingProxyType)A mapping proxy presents a dynamic read-only view over a dictionary.
proxy = User.__dict__
print(proxy["kind"])
User.role = "admin"
print(proxy["role"])The proxy reflects updates to the underlying mapping, but does not allow direct mutation through the proxy.
proxy["x"] = 1raises:
TypeErrorThis is used for type dictionaries because CPython wants controlled mutation paths for class objects.
56.16 Simple Namespace
types.SimpleNamespace is a small mutable object with attribute storage.
from types import SimpleNamespace
cfg = SimpleNamespace(host="localhost", port=5432)
print(cfg.host)
print(cfg.port)It is roughly an object wrapper around a dictionary.
print(cfg.__dict__)Output:
{'host': 'localhost', 'port': 5432}It is useful for simple structured data, tests, configuration objects, and ad hoc namespaces.
Unlike a dataclass, it has no declared fields, no type constraints, and no generated methods beyond basic representation and comparison behavior.
56.17 Dynamic Class Creation
types.new_class() creates a class dynamically.
import types
User = types.new_class("User", (), {}, lambda ns: ns.update({
"kind": "user",
}))
print(User.kind)This mirrors the class creation protocol.
Conceptually:
resolve bases
prepare namespace
execute body callback
call metaclass
return class objectThis is useful for metaprogramming tools that need to construct classes while respecting metaclasses and __prepare__.
56.18 Resolving Bases
types.resolve_bases() supports dynamic base classes.
Some objects can provide __mro_entries__ to change how they appear as bases.
Example concept:
class Alias:
def __mro_entries__(self, bases):
return (dict,)
class C(Alias()):
passDuring class creation, CPython resolves the effective base classes before building the final type.
types.resolve_bases() exposes part of this mechanism.
This matters for generic aliases and advanced class construction tools.
56.19 Generic Alias
types.GenericAlias represents parameterized built-in collection types.
Example:
import types
alias = list[int]
print(type(alias) is types.GenericAlias)
print(alias.__origin__)
print(alias.__args__)Output shape:
True
<class 'list'>
(<class 'int'>,)This object supports runtime representation of expressions such as:
list[int]
dict[str, int]
tuple[int, ...]These are mainly used by typing tools, annotation processing, and runtime introspection.
56.20 Union Type
The expression int | str creates a union type object.
import types
u = int | str
print(isinstance(u, types.UnionType))This supports modern type annotation syntax.
def f(x: int | str):
passRuntime tools can inspect the union through typing helpers such as typing.get_origin() and typing.get_args().
56.21 Descriptor Types
types exposes several descriptor-related types.
Examples include:
GetSetDescriptorType
MemberDescriptorType
MethodDescriptorType
WrapperDescriptorType
MethodWrapperType
ClassMethodDescriptorTypeThese names refer to C-level descriptor objects used by built-in and extension types.
Example:
import types
print(isinstance(str.upper, types.MethodDescriptorType))
print(isinstance(object.__str__, types.WrapperDescriptorType))Descriptors are central to attribute access.
obj.attr
↓
type lookup
↓
descriptor protocol
↓
bound result or raw valueThe descriptor types exposed by types help introspection tools classify built-in behavior that has no Python function object.
56.22 DynamicClassAttribute
types.DynamicClassAttribute is used to route class-level attribute access through __getattr__ while preserving instance-level property behavior.
It is used by modules such as enum.
Simplified example:
from types import DynamicClassAttribute
class C:
@DynamicClassAttribute
def value(self):
return 42This is specialized machinery. Most code should use property.
Its importance is that types contains not only type names, but also helper objects needed by standard library internals.
56.23 prepare_class()
types.prepare_class() exposes the namespace preparation phase of class creation.
Class creation roughly follows:
choose metaclass
prepare namespace
execute class body
create class objectA metaclass may define __prepare__:
class Meta(type):
@classmethod
def __prepare__(cls, name, bases):
return {}types.prepare_class() performs the early metaclass and namespace resolution steps.
This is useful when implementing dynamic class factories that need to match normal class statement behavior.
56.24 Runtime Type Names as Documentation
Many objects used by CPython are easier to understand once named.
| Runtime expression | types name |
|---|---|
type(lambda: None) | FunctionType |
type((lambda: None).__code__) | CodeType |
type((x for x in [])) | GeneratorType |
type(sys._getframe()) | FrameType |
type(module) | ModuleType |
type(cls.__dict__) | MappingProxyType |
These names make introspection code clearer and less fragile.
56.25 Relationship to inspect
types and inspect often work together.
types gives concrete runtime type names.
inspect gives higher-level predicates and metadata extraction.
Example:
import inspect
import types
def f():
pass
print(type(f) is types.FunctionType)
print(inspect.isfunction(f))The inspect version usually expresses intent better. The types version gives direct type identity.
Use inspect when asking semantic questions. Use types when exact runtime type objects matter.
56.26 Relationship to CPython Internals
The types module reflects the object taxonomy of CPython.
It names objects created by core runtime systems:
| CPython subsystem | Runtime types exposed through types |
|---|---|
| Compiler | CodeType |
| Function runtime | FunctionType, MethodType |
| Execution engine | FrameType, TracebackType |
| Generators and async | GeneratorType, CoroutineType, AsyncGeneratorType |
| Import system | ModuleType |
| Object model | descriptor types, MappingProxyType |
| Class creation | new_class, prepare_class, resolve_bases |
| Typing runtime | GenericAlias, UnionType |
This makes types a compact map of CPython’s internal object system.
56.27 Common Mistakes
Avoid exact type checks when subclassing or protocol behavior matters.
Less flexible:
import types
def accepts_function(fn):
return type(fn) is types.FunctionTypeMore flexible:
def accepts_callable(fn):
return callable(fn)Exact type checks reject callable objects, bound methods, built-ins, partial functions, and objects with __call__.
Use exact runtime types only when you really need the concrete object category.
Another mistake is assuming built-ins behave like Python functions:
len.__code__This fails because len is a built-in function implemented in C.
56.28 Chapter Summary
The types module names CPython runtime object types. It exposes function types, code objects, modules, frames, tracebacks, generators, coroutines, closure cells, descriptors, mapping proxies, generic aliases, union types, and class creation helpers.
For CPython internals, types is useful because it gives Python-level names to objects created by the compiler, interpreter, import system, function runtime, object model, and async machinery.