一、核心概念解析

1.1 基础定义:代码的内省工具

inspect模块提供了多种函数来获取活动对象(如模块、类、方法、函数、回溯、框架对象和代码对象)的信息。这个过程通常被称为内省反射

关键点inspect模块让你能够在运行时了解代码的结构,这对于调试、代码生成、文档生成和插件系统等场景非常有用。

1.2 基本语法:常用函数概览

import inspect

# 获取对象信息的基本函数
def example_func(x: int, y: str = "hello") -> float:
    """示例函数"""
    return 3.14

print("=== inspect模块基本使用 ===")

# 1. 获取对象类型
print(f"1. 对象类型: {inspect.isfunction(example_func)}")  # 是否是函数

# 2. 获取源码
print(f"2. 源码行数: {inspect.getsourcelines(example_func)[1]}")  # 起始行号

# 3. 获取签名
print(f"3. 函数签名: {inspect.signature(example_func)}")

# 4. 获取文档
print(f"4. 函数文档: {inspect.getdoc(example_func)}")

# 5. 获取模块成员
print(f"5. 模块成员示例: {dir(inspect)[:5]}")

1.3 核心特点:为什么选择inspect模块?

  1. 功能全面:提供多种内省功能
  2. 使用简单:API直观,学习成本低
  3. 动态分析:可以在运行时获取代码信息
  4. 工具友好:支持IDE、调试器等工具开发

二、应用场景详解

2.1 函数与方法内省

获取函数和方法的详细信息是最常见的需求:

import inspect
from typing import Optional, List

print("=== 函数与方法内省 ===")

# 1. 基本函数内省
def calculate_price(
    base_price: float,
    discount: float = 0.1,
    tax_rate: float = 0.08,
    *,
    currency: str = "USD"
) -> float:
    """计算最终价格
    
    Args:
        base_price: 基础价格
        discount: 折扣率 (0-1)
        tax_rate: 税率
        currency: 货币类型
    
    Returns:
        最终价格
    """
    discounted = base_price * (1 - discount)
    return discounted * (1 + tax_rate)

print("1. 基本函数内省:")
print(f"函数名: {calculate_price.__name__}")
print(f"是否函数: {inspect.isfunction(calculate_price)}")
print(f"是否方法: {inspect.ismethod(calculate_price)}")
print(f"是否内置: {inspect.isbuiltin(calculate_price)}")
print(f"是否协程: {inspect.iscoroutinefunction(calculate_price)}")

# 2. 获取函数签名
print("\n2. 函数签名分析:")
sig = inspect.signature(calculate_price)
print(f"完整签名: {sig}")
print(f"返回类型: {sig.return_annotation}")

print(f"参数详情:")
for name, param in sig.parameters.items():
    print(f"{name}:")
    print(f"类型: {param.annotation}")
    print(f"默认值: {param.default}")
    print(f"类型: {param.kind}")

# 3. 获取源代码
print("\n3.源代码分析:")
source_lines, start_line = inspect.getsourcelines(calculate_price)
print(f"起始行: {start_line}")
print(f"源代码:")
for i, line in enumerate(source_lines[:5], 1):  # 只显示前5行
    print(f"{i:2}: {line.rstrip()}")

# 4. 获取文档
print("\n4.文档分析:")
doc = inspect.getdoc(calculate_price)
print(f"文档字符串:\n{doc}")

comments = inspect.getcomments(calculate_price)
if comments:
    print(f"注释: {comments}")

# 5. 实际应用:参数验证装饰器
print("\n5.实际应用:参数验证装饰器")

def validate_arguments(func):
    """验证函数参数类型的装饰器"""
    def wrapper(*args, **kwargs):
        # 获取函数签名
        sig = inspect.signature(func)
        
        # 绑定参数
        bound = sig.bind(*args, **kwargs)
        bound.apply_defaults()
        
        # 验证参数类型
        for name, value in bound.arguments.items():
            param = sig.parameters[name]
            expected_type = param.annotation
            
            # 跳过没有类型注解或类型为inspect.Parameter.empty的情况
            if expected_type is not inspect.Parameter.empty:
                if not isinstance(value, expected_type):
                    raise TypeError(
                        f"参数 '{name}' 应该是 {expected_type.__name__}, "
                        f"但得到 {type(value).__name__}"
                    )
        
        return func(*args, **kwargs)
    
    return wrapper

@validate_arguments
def process_data(data: List[int], multiplier: float = 1.0) -> List[float]:
    """处理数据"""
    return [x * multiplier for x in data]

print("测试验证装饰器:")
try:
    result = process_data([1, 2, 3], 2.0)
    print(f"成功: {result}")
    
    # 这会引发类型错误
    # process_data("不是列表", 2.0)
except TypeError as e:
    print(f"错误: {e}")

2.2 类与对象内省

分析类和对象的内部结构:

import inspect
from dataclasses import dataclass
from typing import ClassVar

print("=== 类与对象内省 ===")

# 1. 定义示例类
class Animal:
    """动物基类"""
    species_count: ClassVar[int] = 0  # 类变量
    
    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age
        Animal.species_count += 1
    
    def speak(self) -> str:
        """动物叫"""
        return "Some sound"
    
    @classmethod
    def get_count(cls) -> int:
        """获取动物数量"""
        return cls.species_count
    
    @staticmethod
    def is_animal(obj) -> bool:
        """检查是否是动物"""
        return isinstance(obj, Animal)

@dataclass
class Dog(Animal):
    """狗类"""
    breed: str
    
    def speak(self) -> str:
        """狗叫"""
        return "Woof!"
    
    def fetch(self, item: str) -> str:
        """接东西"""
        return f"{self.name} fetched the {item}"

# 2. 类信息
print("1. 类信息:")
print(f"类名: {Dog.__name__}")
print(f"模块: {Dog.__module__}")
print(f"文档: {inspect.getdoc(Dog)}")
print(f"是否类: {inspect.isclass(Dog)}")
print(f"是否抽象类: {inspect.isabstract(Dog)}")

# 3. 获取类层次结构
print("\n2. 类层次结构:")
print(f"父类: {Dog.__bases__}")
print(f"MRO: {Dog.__mro__}")

# 获取所有基类
bases = inspect.getmro(Dog)
print(f"所有基类:")
for i, base in enumerate(bases):
    print(f"  {i}. {base.__name__}")

# 4. 获取类成员
print("\n3. 类成员:")
members = inspect.getmembers(Dog)
print(f"成员数量: {len(members)}")

# 过滤出方法和属性
methods = []
attributes = []
for name, obj in members:
    if not name.startswith('_'):  # 跳过私有成员
        if inspect.isfunction(obj) or inspect.ismethod(obj):
            methods.append(name)
        elif not inspect.isroutine(obj):
            attributes.append(name)

print(f"方法: {methods}")
print(f"属性: {attributes}")

# 5. 获取方法详细信息
print("\n4. 方法详细信息:")
for method_name in ['speak', 'fetch', 'get_count', 'is_animal']:
    if hasattr(Dog, method_name):
        method = getattr(Dog, method_name)
        print(f"\n   方法: {method_name}")
        print(f"  是否方法: {inspect.ismethod(method)}")
        print(f"  是否函数: {inspect.isfunction(method)}")
        print(f"  是否类方法: {inspect.ismethod(method) and method.__self__ is Dog}")
        print(f"  是否静态方法: {isinstance(method, staticmethod)}")
        print(f"  签名: {inspect.signature(method)}")

# 6. 实例内省
print("\n5. 实例内省:")
buddy = Dog(name="Buddy", age=3, breed="Golden Retriever")

# 获取实例属性
instance_attrs = inspect.getmembers(buddy)
print(f"实例属性:")
for name, value in instance_attrs:
    if not name.startswith('_') and not inspect.isroutine(value):
        print(f"  {name}: {value}")

# 获取实例的方法
print(f"\n   实例方法:")
for name, value in instance_attrs:
    if not name.startswith('_') and inspect.isroutine(value):
        print(f"  {name}: {value}")

# 7. 判断对象类型
print("\n6. 对象类型判断:")
print(f"buddy 是 Dog 实例: {isinstance(buddy, Dog)}")
print(f"buddy 是 Animal 实例: {isinstance(buddy, Animal)}")
print(f"Dog 是 Animal 子类: {issubclass(Dog, Animal)}")

2.3 模块与包内省

分析和探索模块的组成:

import inspect
import os
import sys
from typing import Any, List

print("=== 模块与包内省 ===")

# 1. 获取当前模块信息
print("1. 当前模块信息:")

# 获取当前模块
current_module = inspect.currentframe().f_globals['__name__']
print(f"当前模块: {current_module}")

# 获取模块对象
module = sys.modules[current_module]
print(f"模块文件: {module.__file__}")

# 2. 获取模块成员
print("\n2. 模块成员:")
members = inspect.getmembers(module)

# 分类显示
functions = []
classes = []
variables = []

for name, obj in members:
    if not name.startswith('_'):
        if inspect.isfunction(obj):
            functions.append(name)
        elif inspect.isclass(obj):
            classes.append(name)
        elif not inspect.isroutine(obj):
            variables.append(name)

print(f"函数: {functions[:5]}...")  # 只显示前5个
print(f"类: {classes[:5]}...")
print(f"变量: {variables[:5]}...")

# 3. 导入模块并内省
print("\n3. 导入模块内省:")

# 导入os模块进行分析
os_members = inspect.getmembers(os)

# 统计不同类型的成员
os_funcs = []
os_classes = []
os_others = []

for name, obj in os_members:
    if not name.startswith('_'):
        if inspect.isfunction(obj):
            os_funcs.append(name)
        elif inspect.isclass(obj):
            os_classes.append(name)
        elif not inspect.isroutine(obj):
            os_others.append(name)

print(f"os模块统计:")
print(f"  函数数量: {len(os_funcs)}")
print(f"  类数量: {len(os_classes)}")
print(f"  其他成员: {len(os_others)}")

# 显示一些示例函数
print(f"  示例函数: {os_funcs[:5]}")

# 4. 获取模块源码
print("\n4. 模块源码信息:")

def get_module_info(module_name: str):
    """获取模块信息"""
    try:
        module = __import__(module_name)
        
        print(f"模块: {module_name}")
        if hasattr(module, '__file__'):
            print(f"  文件: {module.__file__}")
        
        # 尝试获取源码
        try:
            source = inspect.getsource(module)
            lines = source.count('\n') + 1
            print(f"  源码行数: {lines}")
        except (TypeError, OSError):
            print(f"  源码: 无法获取(可能是内置模块)")
        
        # 获取文档
        doc = inspect.getdoc(module)
        if doc:
            first_line = doc.split('\n')[0]
            print(f"  文档: {first_line}...")
        
    except ImportError:
        print(f"无法导入模块: {module_name}")

# 测试不同模块
get_module_info('os')
get_module_info('collections')

# 5. 动态发现插件
print("\n5. 动态发现插件:")

def discover_plugins(module, base_class=None):
    """在模块中发现所有类"""
    plugins = []
    
    for name, obj in inspect.getmembers(module):
        if inspect.isclass(obj):
            if base_class is None or (base_class and issubclass(obj, base_class)):
                plugins.append((name, obj))
    
    return plugins

# 在os模块中查找类
os_classes = discover_plugins(os)
print(f"os模块中的类: {[name for name, _ in os_classes]}")

# 6. 包的遍历
print("\n6. 包的遍历:")

def explore_package(package_name: str, depth: int = 0):
    """递归探索包结构"""
    try:
        package = __import__(package_name)
        
        # 缩进
        indent = "  " * depth
        
        print(f"{indent}📦 {package_name}")
        
        # 获取包中的模块
        if hasattr(package, '__path__'):
            import pkgutil
            for _, module_name, is_pkg in pkgutil.iter_modules(package.__path__):
                full_name = f"{package_name}.{module_name}"
                
                if is_pkg:
                    explore_package(full_name, depth + 1)
                else:
                    print(f"{indent}  📄 {module_name}")
    
    except ImportError:
        print(f"{indent}⚠ 无法导入: {package_name}")

# 简单演示(不实际递归,避免输出太长)
print("   包结构示例:")
explore_package("os")

三、高级技巧

3.1 调用栈与执行上下文

获取调用栈信息对于调试和日志记录非常有用:

import inspect
import traceback
import sys

print("=== 调用栈与执行上下文 ===")

# 1. 获取当前帧
print("1. 当前执行帧:")

def get_caller_info(level: int = 1) -> dict:
    """获取调用者信息"""
    frame = inspect.currentframe()
    
    # 向上回溯指定层数
    for _ in range(level + 1):
        if frame is None:
            break
        frame = frame.f_back
    
    if frame is None:
        return {}
    
    return {
        'function': frame.f_code.co_name,
        'file': frame.f_code.co_filename,
        'line': frame.f_lineno,
        'locals': dict(frame.f_locals)
    }

def func_a():
    """函数A调用func_b"""
    x = 10
    y = 20
    return func_b()

def func_b():
    """函数B获取调用者信息"""
    # 获取直接调用者(func_a)的信息
    caller = get_caller_info(level=1)
    return caller

print("   调用链分析:")
info = func_a()
print(f"调用者: {info.get('function')}")
print(f"文件: {info.get('file')}")
print(f"行号: {info.get('line')}")
print(f"局部变量: {info.get('locals')}")

# 2. 获取完整调用栈
print("\n2. 完整调用栈:")

def deep_func(n: int):
    """递归函数"""
    if n <= 0:
        # 获取调用栈
        stack = inspect.stack()
        return stack
    return deep_func(n - 1)

# 获取调用栈
stack = deep_func(3)
print(f"调用栈深度: {len(stack)}")

# 显示调用栈信息
for i, frame_info in enumerate(stack[:4]):  # 只显示前4层
    frame, filename, lineno, function, code_context, index = frame_info
    print(f"第{i}层: {function}() 在 {filename}:{lineno}")

# 3. 跟踪异常
print("\n3. 异常跟踪:")

def risky_operation():
    """有风险的运算"""
    return 1 / 0

def safe_wrapper():
    """安全的包装器"""
    try:
        risky_operation()
    except Exception as e:
        # 获取异常的调用栈
        exc_type, exc_value, exc_traceback = sys.exc_info()
        
        # 提取栈帧
        tb = exc_traceback
        frames = []
        while tb is not None:
            frame = tb.tb_frame
            frames.append({
                'function': frame.f_code.co_name,
                'file': frame.f_code.co_filename,
                'line': frame.f_lineno
            })
            tb = tb.tb_next
        
        return frames, str(e)
    
    return [], None

frames, error = safe_wrapper()
if error:
    print(f"错误: {error}")
    print(f"调用栈:")
    for i, frame in enumerate(frames):
        print(f"  第{i}层: {frame['function']}() 在 {frame['file']}:{frame['line']}")

# 4. 性能分析装饰器
print("\n4. 性能分析装饰器:")

import time
from functools import wraps

def profile(func):
    """性能分析装饰器"""
    @wraps(func)
    def wrapper(*args, **kwargs):
        # 获取调用信息
        caller_frame = inspect.currentframe().f_back
        caller_info = {
            'function': caller_frame.f_code.co_name,
            'file': caller_frame.f_code.co_filename,
            'line': caller_frame.f_lineno
        }
        
        # 计时
        start = time.perf_counter()
        result = func(*args, **kwargs)
        elapsed = time.perf_counter() - start
        
        # 输出性能信息
        print(f"⏱  {func.__name__}() 耗时: {elapsed:.6f}秒")
        print(f"调用者: {caller_info['function']}() "
              f"在 {caller_info['file']}:{caller_info['line']}")
        
        return result
    
    return wrapper

@profile
def slow_operation():
    """模拟耗时操作"""
    time.sleep(0.1)
    return "完成"

print("   运行性能测试:")
result = slow_operation()
print(f"结果: {result}")

# 5. 调试装饰器
print("\n5. 调试装饰器:")

def debug(func):
    """调试装饰器"""
    @wraps(func)
    def wrapper(*args, **kwargs):
        # 获取函数签名
        sig = inspect.signature(func)
        
        # 绑定参数
        bound = sig.bind(*args, **kwargs)
        bound.apply_defaults()
        
        print(f"🔍 调用 {func.__name__}()")
        print(f"参数: {dict(bound.arguments)}")
        
        # 执行函数
        result = func(*args, **kwargs)
        
        print(f"结果: {result}")
        return result
    
    return wrapper

@debug
def add_numbers(a: int, b: int) -> int:
    """加法"""
    return a + b

print("   调试示例:")
result = add_numbers(3, 5)
print(f"最终结果: {result}")

3.2 动态代码生成与修改

使用inspect模块生成和修改代码:

import inspect
import ast
import textwrap
from typing import Dict, Any, List

print("=== 动态代码生成与修改 ===")

# 1. 从现有函数生成代码
print("1. 从现有函数生成代码:")

def example_function(x: int, y: str = "test") -> Dict[str, Any]:
    """示例函数"""
    result = {"x": x, "y": y}
    return result

# 获取函数源码
source = inspect.getsource(example_function)
print(f"源码:\n{source}")

# 2. 创建新函数
print("\n2. 创建新函数:")

def create_function(
    name: str,
    params: List[str],
    body: str,
    return_type: str = "None"
) -> str:
    """创建函数定义字符串"""
    params_str = ", ".join(params)
    function_def = f"def {name}({params_str}) -> {return_type}:\n"
    
    # 缩进函数体
    indented_body = textwrap.indent(body, "    ")
    
    return function_def + indented_body

# 定义新函数
new_func_code = create_function(
    name="multiply_all",
    params=["numbers: List[int]", "factor: int = 2"],
    body="return [x * factor for x in numbers]",
    return_type="List[int]"
)

print(f"生成的代码:\n{new_func_code}")

# 3. 动态执行生成的函数
print("\n3. 动态执行生成的函数:")

# 添加必要的导入
exec_code = "from typing import List\n\n" + new_func_code

# 执行代码
exec_globals = {}
exec(exec_code, exec_globals)

# 获取函数
multiply_all = exec_globals['multiply_all']

# 测试函数
result = multiply_all([1, 2, 3, 4], 3)
print(f"运行结果: {result}")

# 检查函数信息
print(f"函数签名: {inspect.signature(multiply_all)}")
print(f"函数文档: {inspect.getdoc(multiply_all)}")

# 4. 修改函数行为
print("\n4. 修改函数行为:")

def original_func(x: int) -> int:
    """原始函数"""
    return x * 2

def create_wrapper(original):
    """创建包装器函数"""
    original_source = inspect.getsource(original)
    
    # 修改函数名
    wrapped_source = original_source.replace(
        original.__name__,
        f"wrapped_{original.__name__}"
    )
    
    # 在函数体前添加日志
    lines = wrapped_source.split('\n')
    for i, line in enumerate(lines):
        if line.strip().startswith('return'):
            # 在return前添加日志
            indent = len(line) - len(line.lstrip())
            log_line = " " * indent + f'print(f"计算: {x} * 2 = {x * 2}")\n'
            lines[i] = log_line + line
            break
    
    return '\n'.join(lines)

wrapped_code = create_wrapper(original_func)
print(f"包装后的代码:\n{wrapped_code}")

# 5. 函数工厂
print("\n5. 函数工厂:")

def function_factory(
    name: str,
    operation: str
):
    """创建数学运算函数"""
    # 定义模板
    template = f"""
def {name}(a: float, b: float) -> float:
    \"\"\"计算 a {operation} b\"\"\"
    return a {operation} b
"""
    
    # 执行模板
    exec_globals = {}
    exec(template, exec_globals)
    
    return exec_globals[name]

# 创建不同的运算函数
add = function_factory("add", "+")
subtract = function_factory("subtract", "-")
multiply = function_factory("multiply", "*")
divide = function_factory("divide", "/")

print(f"创建的运算函数:")
print(f"add(5, 3) = {add(5, 3)}")
print(f"subtract(5, 3) = {subtract(5, 3)}")
print(f"multiply(5, 3) = {multiply(5, 3)}")
print(f"divide(6, 3) = {divide(6, 3)}")

# 6. 装饰器工厂
print("\n6. 装饰器工厂:")

def validator_factory(*validators):
    """创建验证器装饰器"""
    
    def decorator(func):
        # 获取函数签名
        sig = inspect.signature(func)
        params = list(sig.parameters.keys())
        
        def wrapper(*args, **kwargs):
            # 绑定参数
            bound = sig.bind(*args, **kwargs)
            bound.apply_defaults()
            
            # 应用验证器
            for param_name, value in bound.arguments.items():
                if param_name in validators:
                    if not validators[param_name](value):
                        raise ValueError(f"参数 {param_name} 验证失败: {value}")
            
            return func(*args, **kwargs)
        
        return wrapper
    
    return decorator

# 定义验证函数
def is_positive(x):
    return x > 0

def is_even(x):
    return x % 2 == 0

# 创建验证装饰器
validate = validator_factory(x=is_positive, y=is_even)

@validate
def process_numbers(x: int, y: int) -> int:
    """处理数字"""
    return x + y

print(f"验证测试:")
try:
    result = process_numbers(5, 2)
    print(f"成功: {result}")
    
    # 这会失败
    # process_numbers(-1, 2)
except ValueError as e:
    print(f"验证失败: {e}")

四、实战案例:智能API文档生成器

#!/usr/bin/env python3
"""
智能API文档生成器
使用inspect模块自动生成API文档
"""

import inspect
import json
from typing import Dict, List, Any, Optional, Callable
from dataclasses import dataclass, asdict
import textwrap

@dataclass
class ParameterInfo:
    """参数信息"""
    name: str
    type: str
    default: Any
    description: str = ""
    
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典"""
        return asdict(self)

@dataclass
class FunctionInfo:
    """函数信息"""
    name: str
    description: str
    parameters: List[ParameterInfo]
    return_type: str
    return_description: str = ""
    examples: List[str] = None
    
    def __post_init__(self):
        if self.examples is None:
            self.examples = []
    
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典"""
        data = asdict(self)
        data['parameters'] = [p.to_dict() for p in self.parameters]
        return data

@dataclass
class ClassInfo:
    """类信息"""
    name: str
    description: str
    methods: List[FunctionInfo]
    attributes: List[ParameterInfo]
    base_classes: List[str]
    
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典"""
        data = asdict(self)
        data['methods'] = [m.to_dict() for m in self.methods]
        data['attributes'] = [a.to_dict() for a in self.attributes]
        return data

@dataclass
class ModuleInfo:
    """模块信息"""
    name: str
    description: str
    functions: List[FunctionInfo]
    classes: List[ClassInfo]
    version: str = "1.0.0"
    
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典"""
        data = asdict(self)
        data['functions'] = [f.to_dict() for f in self.functions]
        data['classes'] = [c.to_dict() for c in self.classes]
        return data

class APIDocumenter:
    """API文档生成器"""
    
    def __init__(self, module):
        self.module = module
        self.module_info = None
    
    def extract_function_info(self, func: Callable) -> Optional[FunctionInfo]:
        """提取函数信息"""
        try:
            # 获取基本信息
            name = func.__name__
            doc = inspect.getdoc(func) or ""
            
            # 提取描述和示例
            description = ""
            examples = []
            return_description = ""
            
            if doc:
                lines = doc.split('\n')
                description_lines = []
                
                for line in lines:
                    line = line.strip()
                    if line.startswith("返回:"):
                        return_description = line[3:].strip()
                    elif line.startswith("示例:") or line.startswith("例子:"):
                        # 跳过示例标题
                        pass
                    elif line.startswith("    ") or line.startswith("\t"):
                        # 示例代码
                        examples.append(line.strip())
                    else:
                        description_lines.append(line)
                
                description = '\n'.join(description_lines).strip()
            
            # 获取参数信息
            sig = inspect.signature(func)
            parameters = []
            
            for param_name, param in sig.parameters.items():
                # 跳过self参数
                if param_name == 'self':
                    continue
                
                # 获取类型注解
                param_type = "Any"
                if param.annotation is not inspect.Parameter.empty:
                    param_type = self._format_type(param.annotation)
                
                # 获取默认值
                default = None
                if param.default is not inspect.Parameter.empty:
                    default = param.default
                
                parameters.append(ParameterInfo(
                    name=param_name,
                    type=param_type,
                    default=default,
                    description=""
                ))
            
            # 获取返回类型
            return_type = "Any"
            if sig.return_annotation is not inspect.Parameter.empty:
                return_type = self._format_type(sig.return_annotation)
            
            return FunctionInfo(
                name=name,
                description=description,
                parameters=parameters,
                return_type=return_type,
                return_description=return_description,
                examples=examples
            )
            
        except Exception as e:
            print(f"提取函数 {func} 信息时出错: {e}")
            return None
    
    def extract_class_info(self, cls: type) -> Optional[ClassInfo]:
        """提取类信息"""
        try:
            # 获取基本信息
            name = cls.__name__
            doc = inspect.getdoc(cls) or ""
            
            # 获取基类
            base_classes = [base.__name__ for base in cls.__bases__ if base is not object]
            
            # 获取方法
            methods = []
            for method_name, method in inspect.getmembers(cls, inspect.isfunction):
                # 跳过私有方法
                if method_name.startswith('_'):
                    continue
                
                # 跳过不在类中定义的方法(继承的方法)
                if method.__module__ != cls.__module__:
                    continue
                
                method_info = self.extract_function_info(method)
                if method_info:
                    methods.append(method_info)
            
            # 获取属性
            attributes = []
            for attr_name, attr_value in inspect.getmembers(cls):
                # 跳过私有属性和方法
                if attr_name.startswith('_'):
                    continue
                if inspect.isroutine(attr_value):
                    continue
                
                # 获取类型
                attr_type = self._format_type(type(attr_value))
                
                attributes.append(ParameterInfo(
                    name=attr_name,
                    type=attr_type,
                    default=attr_value,
                    description=""
                ))
            
            return ClassInfo(
                name=name,
                description=doc,
                methods=methods,
                attributes=attributes,
                base_classes=base_classes
            )
            
        except Exception as e:
            print(f"提取类 {cls} 信息时出错: {e}")
            return None
    
    def _format_type(self, type_obj) -> str:
        """格式化类型对象"""
        if hasattr(type_obj, '__name__'):
            return type_obj.__name__
        elif hasattr(type_obj, '_name'):
            return type_obj._name
        else:
            return str(type_obj)
    
    def analyze_module(self) -> ModuleInfo:
        """分析模块"""
        module = self.module
        
        # 获取模块基本信息
        name = module.__name__
        doc = inspect.getdoc(module) or ""
        
        # 获取模块版本
        version = getattr(module, '__version__', '1.0.0')
        
        # 分析函数
        functions = []
        for name, obj in inspect.getmembers(module, inspect.isfunction):
            if obj.__module__ == module.__name__:
                func_info = self.extract_function_info(obj)
                if func_info:
                    functions.append(func_info)
        
        # 分析类
        classes = []
        for name, obj in inspect.getmembers(module, inspect.isclass):
            if obj.__module__ == module.__name__:
                class_info = self.extract_class_info(obj)
                if class_info:
                    classes.append(class_info)
        
        self.module_info = ModuleInfo(
            name=name,
            description=doc,
            functions=functions,
            classes=classes,
            version=version
        )
        
        return self.module_info
    
    def generate_markdown(self) -> str:
        """生成Markdown文档"""
        if self.module_info is None:
            self.analyze_module()
        
        info = self.module_info
        lines = []
        
        # 标题
        lines.append(f"# {info.name} 模块文档")
        lines.append(f"\n版本: {info.version}")
        
        # 模块描述
        if info.description:
            lines.append(f"\n## 概述")
            lines.append(f"\n{info.description}")
        
        # 函数文档
        if info.functions:
            lines.append(f"\n## 函数")
            
            for func in info.functions:
                lines.append(f"\n### {func.name}()")
                
                if func.description:
                    lines.append(f"\n{func.description}")
                
                # 参数
                if func.parameters:
                    lines.append(f"\n**参数:**")
                    lines.append("")
                    lines.append("| 参数名 | 类型 | 默认值 | 描述 |")
                    lines.append("|--------|------|--------|------|")
                    
                    for param in func.parameters:
                        default = param.default if param.default is not None else "无"
                        lines.append(f"| {param.name} | {param.type} | {default} | {param.description} |")
                
                # 返回值
                lines.append(f"\n**返回值:** {func.return_type}")
                if func.return_description:
                    lines.append(f"\n{func.return_description}")
                
                # 示例
                if func.examples:
                    lines.append(f"\n**示例:**")
                    lines.append("```python")
                    for example in func.examples:
                        lines.append(example)
                    lines.append("```")
        
        # 类文档
        if info.classes:
            lines.append(f"\n## 类")
            
            for cls in info.classes:
                lines.append(f"\n### {cls.name}")
                
                if cls.description:
                    lines.append(f"\n{cls.description}")
                
                # 基类
                if cls.base_classes:
                    lines.append(f"\n**继承自:** {', '.join(cls.base_classes)}")
                
                # 属性
                if cls.attributes:
                    lines.append(f"\n**属性:**")
                    lines.append("")
                    lines.append("| 属性名 | 类型 | 默认值 | 描述 |")
                    lines.append("|--------|------|--------|------|")
                    
                    for attr in cls.attributes:
                        lines.append(f"| {attr.name} | {attr.type} | {attr.default} | {attr.description} |")
                
                # 方法
                if cls.methods:
                    lines.append(f"\n**方法:**")
                    
                    for method in cls.methods:
                        lines.append(f"\n#### {method.name}()")
                        
                        if method.description:
                            lines.append(f"\n{method.description}")
                        
                        # 方法参数
                        if method.parameters:
                            lines.append(f"\n**参数:**")
                            lines.append("")
                            lines.append("| 参数名 | 类型 | 默认值 | 描述 |")
                            lines.append("|--------|------|--------|------|")
                            
                            for param in method.parameters:
                                default = param.default if param.default is not None else "无"
                                lines.append(f"| {param.name} | {param.type} | {default} | {param.description} |")
        
        return '\n'.join(lines)
    
    def generate_json(self) -> str:
        """生成JSON文档"""
        if self.module_info is None:
            self.analyze_module()
        
        return json.dumps(
            self.module_info.to_dict(),
            indent=2,
            ensure_ascii=False,
            default=str
        )

# 示例模块
"""
示例数学工具模块
"""

__version__ = "1.0.0"

def add(a: float, b: float) -> float:
    """
    加法运算
    
    参数:
        a: 第一个数
        b: 第二个数
    
    返回:
        两数之和
    
    示例:
        add(1, 2)  # 返回 3
    """
    return a + b

def multiply(numbers: list, factor: float = 1.0) -> list:
    """
    列表乘法
    
    将列表中的每个元素乘以指定的因子
    
    参数:
        numbers: 数字列表
        factor: 乘法因子,默认为1.0
    
    返回:
        乘以因子后的新列表
    """
    return [x * factor for x in numbers]

class Calculator:
    """
    计算器类
    
    提供基本的数学运算功能
    """
    
    version = "1.0"
    
    def __init__(self, name: str = "默认计算器"):
        """
        初始化计算器
        
        参数:
            name: 计算器名称
        """
        self.name = name
    
    def add(self, a: float, b: float) -> float:
        """
        加法
        
        参数:
            a: 第一个数
            b: 第二个数
        
        返回:
            两数之和
        """
        return a + b
    
    def get_info(self) -> dict:
        """
        获取计算器信息
        
        返回:
            包含计算器信息的字典
        """
        return {
            "name": self.name,
            "version": self.version
        }

# 使用示例
if __name__ == "__main__":
    print("=== 智能API文档生成器演示 ===\n")
    
    # 导入示例模块
    import sys
    import types
    
    # 创建模块对象
    module = types.ModuleType("math_tools")
    module.__dict__.update(globals())
    
    # 创建文档生成器
    documenter = APIDocumenter(module)
    
    # 分析模块
    print("分析模块...")
    module_info = documenter.analyze_module()
    
    print(f"模块: {module_info.name}")
    print(f"版本: {module_info.version}")
    print(f"函数数量: {len(module_info.functions)}")
    print(f"类数量: {len(module_info.classes)}")
    
    # 生成Markdown文档
    print("\n生成Markdown文档:")
    markdown = documenter.generate_markdown()
    print("\n" + "="*60)
    print(markdown[:500] + "...")  # 只显示前500字符
    print("="*60)
    
    # 生成JSON文档
    print("\n生成JSON文档:")
    json_doc = documenter.generate_json()
    print("\n" + "="*60)
    print(json_doc[:500] + "...")  # 只显示前500字符
    print("="*60)
    
    # 保存文档
    with open("api_documentation.md", "w", encoding="utf-8") as f:
        f.write(markdown)
    print("\nMarkdown文档已保存到: api_documentation.md")
    
    with open("api_documentation.json", "w", encoding="utf-8") as f:
        f.write(json_doc)
    print("JSON文档已保存到: api_documentation.json")

五、注意事项

5.1 使用限制

  1. 性能开销:内省操作有一定性能开销,避免在性能关键路径中使用
  2. 安全风险exec()eval()可能执行任意代码,需谨慎使用
  3. 私有成员:无法直接访问某些私有成员(以双下划线开头)
  4. 动态代码:对动态生成的代码支持有限

5.2 常见问题

Q: inspect模块会影响性能吗?

A: 会有一定开销,特别是在频繁调用时。在性能关键代码中谨慎使用。

Q: 如何获取私有方法和属性?

A: 可以通过名称直接访问,但这不是推荐的做法。

Q: inspect和__dict__有什么区别?

A: inspect提供更高级的抽象,__dict__是底层的属性字典。

Q: 如何处理内置函数和C扩展?

A: inspect对内置函数和C扩展的支持有限,可能无法获取源码等信息。

Q: 如何避免循环导入?

A: 在需要时动态导入模块,或使用字符串形式的导入。

5.3 替代方案

  1. __annotations__:获取类型注解
  2. __dict__:直接访问对象的属性字典
  3. dir():列出对象的属性和方法
  4. type()isinstance():类型检查
  5. ast模块:更底层的语法树分析

六、总结

inspect模块是Python中强大的内省工具,它提供了:

  • 对象检查:检查函数、方法、类、模块
  • 源码分析:获取源代码、文档字符串
  • 签名解析:分析函数参数和返回值
  • 调用栈:获取执行上下文和调用栈
  • 动态分析:运行时分析和修改代码

核心价值:

  1. 调试助手:帮助理解和调试复杂代码
  2. 文档生成:自动生成API文档
  3. 代码分析:分析和理解代码结构
  4. 动态编程:实现插件系统、代码生成等高级功能
  5. 工具开发:支持IDE、调试器等开发工具

给你的建议:

  1. 在需要动态分析代码时使用inspect
  2. 谨慎使用exec/eval,避免安全风险
  3. 缓存内省结果以提高性能
  4. 结合typing模块获得更好的类型信息
  5. 在文档生成和调试工具中充分利用inspect

实用技巧回顾:

  1. 使用inspect.signature()获取函数签名
  2. 使用inspect.getsource()获取源代码
  3. 使用inspect.stack()获取调用栈
  4. 使用inspect.getmembers()获取对象成员
  5. 使用inspect.isclass()等函数判断对象类型

掌握inspect模块,你将能够编写更智能、更灵活的Python代码。无论是开发工具、分析代码,还是实现高级的元编程功能,inspect都是不可或缺的利器。


思考与实践:

  1. 为你的项目编写一个自动生成文档的工具
  2. 实现一个装饰器,记录函数的调用次数和执行时间
  3. 创建一个简单的代码分析工具,统计模块中的函数和类数量
  4. 尝试实现一个简单的插件系统,动态加载和发现插件

互动环节:

你在使用inspect模块时有什么心得或技巧?或者遇到过什么有趣的内省问题?欢迎在评论区分享!

Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐