# 56. `types`

# 56. `types`

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:

```python
import types

def f():
    pass

print(isinstance(f, types.FunctionType))
print(isinstance(f.__code__, types.CodeType))
```

Output:

```text
True
True
```

Conceptually:

```text
runtime object
    ↓
type(obj)
    ↓
named runtime type from types
```

The module is useful when writing tools that need to distinguish between Python-level object categories:

```text
debuggers
profilers
serializers
documentation generators
import tools
decorator frameworks
plugin loaders
mocking libraries
runtime validators
```

## 56.2 `types` vs `type`

The built-in `type()` function returns the concrete type of an object.

```python
def f():
    pass

print(type(f))
```

Output shape:

```text
<class 'function'>
```

The `types` module gives a named reference to that same type:

```python
import types

print(type(f) is types.FunctionType)
```

Output:

```text
True
```

So this:

```python
type(f) is type(lambda: None)
```

can be written more clearly as:

```python
type(f) is types.FunctionType
```

Most 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`.

```python
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:

```text
function object
    code object
    globals
    defaults
    closure
```

`types.FunctionType` can also construct a new function from a code object.

```python
import types

def template():
    return answer

code = template.__code__
namespace = {"answer": 42}

fn = types.FunctionType(code, namespace)
print(fn())
```

Output:

```text
42
```

This shows that a function combines compiled code with a global namespace.

## 56.4 Code Objects

Code objects have type `types.CodeType`.

```python
import types

def f(x):
    return x + 1

print(isinstance(f.__code__, types.CodeType))
```

A code object is immutable compiled program data.

It contains:

```text
bytecode
constants
names
local variable names
free variable names
cell variable names
source filename
line information
exception table
stack size
flags
```

Code objects do not store runtime local values. They describe executable code.

```python
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.

```text
source code
    ↓
compiler
    ↓
code object
    ↓
function object
    ↓
frame execution
```

## 56.5 Creating Modified Code Objects

Code objects are immutable, but modern Python provides `code.replace()`.

```python
def f():
    return 1

code = f.__code__
new_code = code.replace(co_name="renamed")

print(new_code.co_name)
```

Output:

```text
renamed
```

This is safer than manually calling `types.CodeType(...)`, because the constructor signature changes across Python versions.

Use cases include:

```text
debugging tools
coverage tools
bytecode experiments
code generation systems
function wrappers
educational bytecode work
```

For production code, code object mutation should be treated as CPython-version-sensitive.

## 56.6 Method Types

Bound Python methods have type `types.MethodType`.

```python
import types

class User:
    def name(self):
        return "anonymous"

u = User()

print(isinstance(u.name, types.MethodType))
```

A bound method packages:

```text
function
instance
```

That is:

```python
print(u.name.__func__)
print(u.name.__self__)
```

Conceptually:

```text
obj.method
    ↓
descriptor lookup
    ↓
bound method(function, obj)
```

Calling:

```python
u.name()
```

is equivalent in effect to:

```python
User.name(u)
```

`types.MethodType` can also bind a function to an object manually:

```python
import types

class User:
    pass

def greet(self):
    return "hello"

u = User()
u.greet = types.MethodType(greet, u)

print(u.greet())
```

Output:

```text
hello
```

## 56.7 Built-in Function and Method Types

C-level built-ins have separate runtime types.

```python
import types

print(isinstance(len, types.BuiltinFunctionType))
print(isinstance([].append, types.BuiltinMethodType))
```

Built-in functions usually have no Python code object:

```python
print(hasattr(len, "__code__"))
```

Output:

```text
False
```

They are implemented in C and exposed as callable Python objects.

Conceptually:

```text
Python call
    ↓
built-in callable object
    ↓
C function pointer
```

This 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`.

```python
import types
import math

print(isinstance(math, types.ModuleType))
```

You can create a module object directly:

```python
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.

```text
sys.modules["math"] → module object
```

## 56.9 Frame Type

Frame objects have type `types.FrameType`.

```python
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:

```text
tracebacks
debuggers
profilers
inspect.currentframe()
sys._getframe()
generators
coroutines
```

Holding 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`.

```python
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:

```text
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`.

```python
import types

def gen():
    yield 1

g = gen()

print(isinstance(g, types.GeneratorType))
```

A generator object contains:

```text
code object
suspended frame
running flag
delegated iterator
```

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

```text
call generator function
    ↓
create generator object
    ↓
start execution on next()
    ↓
pause at yield
    ↓
resume later
```

## 56.12 Coroutine Type

Native coroutine objects have type `types.CoroutineType`.

```python
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`.

```python
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:

```python
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`.

```python
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:

```text
outer local variable
    ↓
cell object
    ↓
inner function closure
```

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

```python
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.

```python
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.

```python
proxy["x"] = 1
```

raises:

```text
TypeError
```

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

```python
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.

```python
print(cfg.__dict__)
```

Output:

```text
{'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.

```python
import types

User = types.new_class("User", (), {}, lambda ns: ns.update({
    "kind": "user",
}))

print(User.kind)
```

This mirrors the class creation protocol.

Conceptually:

```text
resolve bases
prepare namespace
execute body callback
call metaclass
return class object
```

This 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:

```python
class Alias:
    def __mro_entries__(self, bases):
        return (dict,)

class C(Alias()):
    pass
```

During 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:

```python
import types

alias = list[int]

print(type(alias) is types.GenericAlias)
print(alias.__origin__)
print(alias.__args__)
```

Output shape:

```text
True
<class 'list'>
(<class 'int'>,)
```

This object supports runtime representation of expressions such as:

```python
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.

```python
import types

u = int | str

print(isinstance(u, types.UnionType))
```

This supports modern type annotation syntax.

```python
def f(x: int | str):
    pass
```

Runtime 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:

```text
GetSetDescriptorType
MemberDescriptorType
MethodDescriptorType
WrapperDescriptorType
MethodWrapperType
ClassMethodDescriptorType
```

These names refer to C-level descriptor objects used by built-in and extension types.

Example:

```python
import types

print(isinstance(str.upper, types.MethodDescriptorType))
print(isinstance(object.__str__, types.WrapperDescriptorType))
```

Descriptors are central to attribute access.

```text
obj.attr
    ↓
type lookup
    ↓
descriptor protocol
    ↓
bound result or raw value
```

The 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:

```python
from types import DynamicClassAttribute

class C:
    @DynamicClassAttribute
    def value(self):
        return 42
```

This 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:

```text
choose metaclass
prepare namespace
execute class body
create class object
```

A metaclass may define `__prepare__`:

```python
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:

```python
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:

```python
import types

def accepts_function(fn):
    return type(fn) is types.FunctionType
```

More flexible:

```python
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:

```python
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.
