Skip to content

Introspection & Reflection

Overview

Introspection examines code at runtime:

  • inspect module: Powerful runtime reflection
  • dir(), vars(), type(): Basic inspection
  • Source code access: Get function source
  • Signature inspection: Analyze function signatures

Basic Introspection

dir() and vars()

class Example:
 class_var = 42

 def __init__(self):
 self.instance_var = 10

 def method(self):
 pass

obj = Example()

# List all attributes
print(dir(obj)) # ['__class__', '__init__',..., 'class_var', 'instance_var', 'method']

# Get attribute dictionary
print(vars(obj)) # {'instance_var': 10}

# Get type
print(type(obj)) # <class '__main__.Example'>

Checking Properties

import inspect

def my_function(x, y=10):
 """Example function."""
 return x + y

# Check if callable
print(callable(my_function)) # True
print(callable(42)) # False

# Check if function or method
print(inspect.isfunction(my_function)) # True

# Check if class
print(inspect.isclass(Example)) # True

The inspect Module

Function Signature

import inspect

def train(model, data, epochs=10, learning_rate=0.001):
 """Train a model."""
 pass

# Get signature
sig = inspect.signature(train)
print(sig) # (model, data, epochs=10, learning_rate=0.001)

# Inspect parameters
for param_name, param in sig.parameters.items():
 print(f"{param_name}: {param.default}")

# Get return annotation
print(sig.return_annotation) # None (no annotation)

Function Source

import inspect

def my_function():
 """Get this function's source."""
 return 42

# Get source code
source = inspect.getsource(my_function)
print(source)
# def my_function():
# """Get this function's source."""
# return 42

# Get source lines
lines, line_num = inspect.getsourcelines(my_function)
print(f"Function at line {line_num}")

Class Inspection

import inspect

class Base:
 def base_method(self):
 pass

class Derived(Base):
 def derived_method(self):
 pass

# Get base classes
print(inspect.getmro(Derived)) # (Derived, Base, object)

# Get members
members = inspect.getmembers(Derived, predicate=inspect.ismethod)
for name, method in members:
 print(f"{name}: {method}")

# Get source of class
source = inspect.getsource(Derived)

Dynamic Dispatch

Method Lookup

import inspect

class Model:
 def forward(self, x):
 return x * 2

 def backward(self, grad):
 return grad * 2

model = Model()

# Get method dynamically
method_name = "forward"
method = getattr(model, method_name)

# Call it
result = method(5) # 10

# Check if method exists
if hasattr(model, 'forward'):
 print("Model has forward method")

Registry Pattern with Introspection

import inspect

class Registry:
 def __init__(self):
 self.handlers = {}

 def register(self, name):
 """Decorator to register handler."""
 def decorator(func):
 self.handlers[name] = func
 return func
 return decorator

 def register_all(self, obj):
 """Auto-register all methods starting with 'handle_'."""
 for name, method in inspect.getmembers(obj, predicate=inspect.ismethod):
 if name.startswith('handle_'):
 event = name[7:] # Remove 'handle_'
 self.handlers[event] = method

registry = Registry()

class EventHandler:
 def handle_click(self, event):
 print(f"Click: {event}")

 def handle_scroll(self, event):
 print(f"Scroll: {event}")

handler = EventHandler()
registry.register_all(handler)

# Use registry
print(registry.handlers) # {'click': method, 'scroll': method}

Performance Introspection

Profiling Information

import inspect

def decorated_function(x):
 """Get info about function."""
 return x * 2

# Get function name
print(inspect.getfullargspec(decorated_function).args) # ['x']

# Get docstring
print(inspect.getdoc(decorated_function))

# Count lines
lines = inspect.getsourcelines(decorated_function)[0]
print(f"{len(lines)} lines of code")

Real-World ML Example

Auto-Configurable Model

import inspect

class ModelFactory:
 @staticmethod
 def create(class_name, **kwargs):
 """Create model from class name."""
 # Get class from registry
 model_class = globals()[class_name]

 # Get signature
 sig = inspect.signature(model_class.__init__)

 # Filter kwargs to valid parameters
 valid_kwargs = {}
 for param_name in sig.parameters:
 if param_name != 'self' and param_name in kwargs:
 valid_kwargs[param_name] = kwargs[param_name]

 # Create instance
 return model_class(**valid_kwargs)

class MLPModel:
 def __init__(self, hidden_size=256, num_layers=3):
 self.hidden_size = hidden_size
 self.num_layers = num_layers

# Auto-create with kwargs
model = ModelFactory.create('MLPModel', hidden_size=512, num_layers=5, unknown_param=123)
print(model.hidden_size) # 512
print(model.num_layers) # 5
# unknown_param is ignored

Summary: Introspection Tools

Tool Use Case
dir() List attributes
vars() Get attribute dict
type() Get object type
inspect module Deep reflection
getattr/setattr Dynamic access

-

  • [01 Getattr Setattr & Proxies](/05-py3/06-dynamic-features/(01-getattr-setattr-proxies/) - Dynamic attribute access
  • [02 Execution Model & Compilation](/05-py3/09-bytecode-and-execution/(02-execution-model-compilation/) - Runtime behavior
  • [05 Metaclasses & Advanced Oop](/05-py3/02-object-oriented-patterns/(05-metaclasses-advanced-oop/) - Type introspection