Skip to main content

types — Dynamic Type Creation and Names for Built-in Types

📚 Official Documentation & Resources

Primary Official Sources (REQUIRED)

Additional Authoritative Sources

IMPORTANT: Examples in this guide are adapted from the official Python documentation at https://docs.python.org/3/library/types.html

Overview

The types module defines utility functions and classes for working with types and dynamic type creation. This module provides names for types that are not directly accessible as built-ins, such as the type of generator functions, and utilities for creating new types dynamically. It's particularly useful for metaprogramming, type introspection, and creating dynamic code structures.

The module has been available since early Python versions and has been enhanced over time with additional type utilities, especially for supporting modern Python features like async/await, generics, and type annotations.

🎯 Key Characteristics

  • Type Introspection: Access to internal Python types not available as built-ins
  • Dynamic Type Creation: Utilities for creating types at runtime
  • Function Types: Support for various function and method types
  • Generator Support: Types for generators, coroutines, and async iterators
  • Namespace Utilities: Dynamic namespace creation and manipulation
  • Metaclass Support: Utilities for working with metaclasses and type creation

🔧 Prerequisites and Setup

Python Version Compatibility

  • Minimum Python version required: Python 2.0+ (basic functionality)
  • Async support: Python 3.5+ (coroutine types)
  • Enhanced features: Python 3.7+ (additional type utilities)
  • Latest improvements: Python 3.9+ (generic alias support)

Installation and Imports

# Standard library (no installation needed)
import types
from types import SimpleNamespace, MethodType, FunctionType

📚 Basic Usage

Official Documentation Examples

Source: All examples adapted from https://docs.python.org/3/library/types.html

Simple Example

import types

# Create a simple namespace
namespace = types.SimpleNamespace()
namespace.x = 1
namespace.y = 2
print(f"Namespace: x={namespace.x}, y={namespace.y}") # Namespace: x=1, y=2

# Check function types
def my_function():
pass

print(isinstance(my_function, types.FunctionType)) # True
print(isinstance(len, types.BuiltinFunctionType)) # True

# Create a method dynamically
class MyClass:
pass

def new_method(self):
return "Hello from dynamic method!"

MyClass.dynamic_method = new_method
obj = MyClass()
print(obj.dynamic_method()) # Hello from dynamic method!

Core Methods/Functions

import types

# SimpleNamespace usage (from official docs)
ns = types.SimpleNamespace(x=1, y=2, z=3)
print(ns) # namespace(x=1, y=2, z=3)
print(ns.x) # 1

# Modify namespace
ns.w = 4
del ns.z
print(ns) # namespace(x=1, y=2, w=4)

# Type checking utilities (from official docs)
def regular_function():
return "regular"

def generator_function():
yield 1

async def coroutine_function():
return "coroutine"

print(isinstance(regular_function, types.FunctionType)) # True
print(isinstance(generator_function(), types.GeneratorType)) # True
print(isinstance(coroutine_function(), types.CoroutineType)) # True

Common Patterns

# Pattern 1: Dynamic class creation (from official docs)
import types

def dynamic_method(self):
return f"Hello from {self.__class__.__name__}!"

# Create class dynamically
DynamicClass = types.new_class(
'DynamicClass',
(object,),
{},
lambda ns: ns.update({'dynamic_method': dynamic_method})
)

obj = DynamicClass()
print(obj.dynamic_method()) # Hello from DynamicClass!

# Pattern 2: Module creation at runtime (from official docs)
module = types.ModuleType('dynamic_module')
module.variable = "Dynamic content"
module.function = lambda: "Dynamic function"

print(module.variable) # Dynamic content
print(module.function()) # Dynamic function

# Pattern 3: Method binding (from official docs)
class Calculator:
def __init__(self, value):
self.value = value

def add_method(self, x):
return self.value + x

# Bind method to class
Calculator.add = add_method

calc = Calculator(10)
print(calc.add(5)) # 15

# Bind method to instance
def multiply_method(self, x):
return self.value * x

calc.multiply = types.MethodType(multiply_method, calc)
print(calc.multiply(3)) # 30

🔧 types API Reference

Core Type Objects

TypeDescriptionExample
FunctionTypeType of user-defined functionsisinstance(func, types.FunctionType)
LambdaTypeType of lambda functions (alias for FunctionType)isinstance(lambda: None, types.LambdaType)
MethodTypeType of bound methodsisinstance(obj.method, types.MethodType)
BuiltinFunctionTypeType of built-in functionsisinstance(len, types.BuiltinFunctionType)
BuiltinMethodTypeType of built-in methodsisinstance([].append, types.BuiltinMethodType)

Generator and Coroutine Types

TypeDescriptionExample
GeneratorTypeType of generator objectsisinstance((x for x in []), types.GeneratorType)
CoroutineTypeType of coroutine objectsisinstance(async_func(), types.CoroutineType)
AsyncGeneratorTypeType of async generator objectsisinstance(async_gen(), types.AsyncGeneratorType)

Class and Module Types

TypeDescriptionExample
ModuleTypeType of modulesisinstance(sys, types.ModuleType)
CodeTypeType of code objectsisinstance(func.__code__, types.CodeType)
FrameTypeType of frame objectsisinstance(sys._getframe(), types.FrameType)
TracebackTypeType of traceback objectsisinstance(tb, types.TracebackType)

Utility Classes and Functions

Function/ClassDescriptionExample
SimpleNamespaceSimple namespace classtypes.SimpleNamespace(x=1, y=2)
new_class(name, bases, kwds, exec_body)Create class dynamicallytypes.new_class('MyClass', (), {})
prepare_class(name, bases, kwds)Prepare class namespaceAdvanced metaclass use
resolve_bases(bases)Resolve MRO base classesMetaclass implementation

Detailed Method Examples

SimpleNamespace Usage

import types

# Official docs example: SimpleNamespace
# Create namespace with initial attributes
config = types.SimpleNamespace(
debug=True,
database_url="localhost:5432",
timeout=30
)

print(config.debug) # True
print(config.database_url) # localhost:5432

# Modify attributes
config.debug = False
config.new_setting = "value"

# Convert to dict
settings_dict = vars(config)
print(settings_dict) # {'debug': False, 'database_url': 'localhost:5432', 'timeout': 30, 'new_setting': 'value'}

# Create from dict
data = {'x': 10, 'y': 20}
point = types.SimpleNamespace(**data)
print(f"Point: ({point.x}, {point.y})") # Point: (10, 20)

Dynamic Class Creation

import types

# Official docs example: new_class function
def class_body(namespace):
namespace['class_variable'] = "Hello"

def instance_method(self):
return f"Instance method called on {self}"

namespace['instance_method'] = instance_method

# Create class dynamically
DynamicClass = types.new_class(
'DynamicClass',
(object,), # base classes
{}, # keyword arguments to metaclass
class_body # function to populate namespace
)

# Use the dynamic class
obj = DynamicClass()
print(obj.class_variable) # Hello
print(obj.instance_method()) # Instance method called on <__main__.DynamicClass object at 0x...>

# Equivalent to:
class RegularClass:
class_variable = "Hello"

def instance_method(self):
return f"Instance method called on {self}"

Method Binding and Types

import types

# Official docs example: Method types and binding
class MyClass:
def __init__(self, value):
self.value = value

def instance_method(self):
return f"Instance method: {self.value}"

obj = MyClass("test")

# Different method types
unbound_method = MyClass.instance_method
bound_method = obj.instance_method

print(isinstance(unbound_method, types.FunctionType)) # True
print(isinstance(bound_method, types.MethodType)) # True

# Create method dynamically
def new_method(self, multiplier):
return self.value * multiplier

# Bind to instance
obj.dynamic_method = types.MethodType(new_method, obj)
print(obj.dynamic_method(3)) # testtesttest

# Bind to class
MyClass.class_method = new_method
obj2 = MyClass("hello")
print(obj2.class_method(2)) # hellohello

Module Creation and Manipulation

import types
import sys

# Official docs example: Dynamic module creation
module_name = 'dynamic_utilities'
utilities = types.ModuleType(module_name)

# Add attributes to module
utilities.PI = 3.14159
utilities.E = 2.71828

def calculate_circle_area(radius):
return utilities.PI * radius ** 2

utilities.calculate_circle_area = calculate_circle_area

# Add module to sys.modules for importing
sys.modules[module_name] = utilities

# Now can import it
import dynamic_utilities
print(dynamic_utilities.calculate_circle_area(5)) # 78.53975

# Module introspection
print(isinstance(utilities, types.ModuleType)) # True
print(utilities.__name__) # dynamic_utilities

Type Checking and Introspection

import types
import inspect

# Official docs example: Type checking utilities
def regular_function(x):
return x * 2

def generator_function():
for i in range(3):
yield i

async def async_function():
return "async result"

class MyClass:
def method(self):
pass

obj = MyClass()

# Function type checking
print(isinstance(regular_function, types.FunctionType)) # True
print(isinstance(lambda x: x, types.LambdaType)) # True (alias for FunctionType)
print(isinstance(len, types.BuiltinFunctionType)) # True
print(isinstance(obj.method, types.MethodType)) # True

# Generator and coroutine checking
gen = generator_function()
coro = async_function()

print(isinstance(gen, types.GeneratorType)) # True
print(isinstance(coro, types.CoroutineType)) # True

# Code object inspection
code = regular_function.__code__
print(isinstance(code, types.CodeType)) # True
print(f"Function name: {code.co_name}") # Function name: regular_function
print(f"Argument count: {code.co_argcount}") # Argument count: 1

Important Notes

Performance Considerations

  • Dynamic type creation has overhead compared to static class definition
  • SimpleNamespace is faster than creating custom classes for simple data containers
  • Type checking with isinstance() is generally fast for built-in types
  • Frame introspection can be expensive and should be used sparingly

Memory Usage

import types
import sys

# SimpleNamespace vs dict memory comparison
data = {'a': 1, 'b': 2, 'c': 3}
namespace = types.SimpleNamespace(**data)

print(f"Dict size: {sys.getsizeof(data)} bytes")
print(f"SimpleNamespace size: {sys.getsizeof(namespace)} bytes")
# SimpleNamespace typically uses slightly more memory but provides attribute access

Common Gotchas

import types

# Gotcha 1: Function vs Lambda type
def func(): pass
lamb = lambda: None

# These are the same type!
print(types.FunctionType is types.LambdaType) # True
print(isinstance(func, types.LambdaType)) # True
print(isinstance(lamb, types.FunctionType)) # True

# Gotcha 2: Method binding
class MyClass:
def method(self): pass

obj = MyClass()

# Unbound vs bound methods
unbound = MyClass.method # Function type
bound = obj.method # Method type

print(isinstance(unbound, types.FunctionType)) # True
print(isinstance(bound, types.MethodType)) # True

# Gotcha 3: SimpleNamespace equality
ns1 = types.SimpleNamespace(x=1, y=2)
ns2 = types.SimpleNamespace(x=1, y=2)
print(ns1 == ns2) # False - they're different objects
print(vars(ns1) == vars(ns2)) # True - same content

Best Practices

# 1. Use SimpleNamespace for configuration and data containers
config = types.SimpleNamespace(
debug=True,
timeout=30,
max_retries=3
)

# 2. Type checking patterns
def process_callable(func):
if isinstance(func, (types.FunctionType, types.MethodType)):
return func()
elif isinstance(func, types.BuiltinFunctionType):
return func()
else:
raise TypeError("Expected callable")

# 3. Dynamic class creation for factories
def create_model_class(name, fields):
def __init__(self, **kwargs):
for field in fields:
setattr(self, field, kwargs.get(field))

def __repr__(self):
field_strs = [f"{field}={getattr(self, field, None)}" for field in fields]
return f"{name}({', '.join(field_strs)})"

return types.new_class(
name,
(object,),
{},
lambda ns: ns.update({'__init__': __init__, '__repr__': __repr__})
)

# 4. Safe frame inspection
def safe_get_caller_name():
try:
frame = sys._getframe(1)
return frame.f_code.co_name
except (AttributeError, ValueError):
return "unknown"

Thread Safety

  • Type objects themselves are thread-safe and immutable
  • SimpleNamespace objects are not thread-safe for concurrent modification
  • Dynamic class creation should generally be done at module load time
  • Frame inspection is generally safe but can be affected by thread switches

Integration with Other Modules

import types
import inspect
import typing

# With inspect module
def analyze_function(func):
if isinstance(func, types.FunctionType):
sig = inspect.signature(func)
print(f"Function: {func.__name__}")
print(f"Parameters: {list(sig.parameters.keys())}")
print(f"Annotations: {func.__annotations__}")

# With typing module
def create_typed_namespace(**kwargs) -> types.SimpleNamespace:
return types.SimpleNamespace(**kwargs)

# With dataclasses (alternative approach)
from dataclasses import dataclass

@dataclass
class TypedConfig:
debug: bool = False
timeout: int = 30

# SimpleNamespace for untyped, dataclass for typed configurations
  • inspect: For detailed object introspection and signature analysis
  • typing: For type hints and generic type support
  • abc: For abstract base classes and interface definition
  • dataclasses: For structured data classes (alternative to SimpleNamespace)
  • collections: For specialized container types and named tuples