Python代码“显微镜“:inspect模块
·
一、核心概念解析
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模块?
- 功能全面:提供多种内省功能
- 使用简单:API直观,学习成本低
- 动态分析:可以在运行时获取代码信息
- 工具友好:支持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 使用限制
- 性能开销:内省操作有一定性能开销,避免在性能关键路径中使用
- 安全风险:
exec()和eval()可能执行任意代码,需谨慎使用 - 私有成员:无法直接访问某些私有成员(以双下划线开头)
- 动态代码:对动态生成的代码支持有限
5.2 常见问题
Q: inspect模块会影响性能吗?
A: 会有一定开销,特别是在频繁调用时。在性能关键代码中谨慎使用。
Q: 如何获取私有方法和属性?
A: 可以通过名称直接访问,但这不是推荐的做法。
Q: inspect和__dict__有什么区别?
A: inspect提供更高级的抽象,__dict__是底层的属性字典。
Q: 如何处理内置函数和C扩展?
A: inspect对内置函数和C扩展的支持有限,可能无法获取源码等信息。
Q: 如何避免循环导入?
A: 在需要时动态导入模块,或使用字符串形式的导入。
5.3 替代方案
__annotations__:获取类型注解__dict__:直接访问对象的属性字典dir():列出对象的属性和方法type()和isinstance():类型检查ast模块:更底层的语法树分析
六、总结
inspect模块是Python中强大的内省工具,它提供了:
- ✅ 对象检查:检查函数、方法、类、模块
- ✅ 源码分析:获取源代码、文档字符串
- ✅ 签名解析:分析函数参数和返回值
- ✅ 调用栈:获取执行上下文和调用栈
- ✅ 动态分析:运行时分析和修改代码
核心价值:
- 调试助手:帮助理解和调试复杂代码
- 文档生成:自动生成API文档
- 代码分析:分析和理解代码结构
- 动态编程:实现插件系统、代码生成等高级功能
- 工具开发:支持IDE、调试器等开发工具
给你的建议:
- 在需要动态分析代码时使用inspect
- 谨慎使用exec/eval,避免安全风险
- 缓存内省结果以提高性能
- 结合typing模块获得更好的类型信息
- 在文档生成和调试工具中充分利用inspect
实用技巧回顾:
- 使用
inspect.signature()获取函数签名 - 使用
inspect.getsource()获取源代码 - 使用
inspect.stack()获取调用栈 - 使用
inspect.getmembers()获取对象成员 - 使用
inspect.isclass()等函数判断对象类型
掌握inspect模块,你将能够编写更智能、更灵活的Python代码。无论是开发工具、分析代码,还是实现高级的元编程功能,inspect都是不可或缺的利器。
思考与实践:
- 为你的项目编写一个自动生成文档的工具
- 实现一个装饰器,记录函数的调用次数和执行时间
- 创建一个简单的代码分析工具,统计模块中的函数和类数量
- 尝试实现一个简单的插件系统,动态加载和发现插件
互动环节:
你在使用inspect模块时有什么心得或技巧?或者遇到过什么有趣的内省问题?欢迎在评论区分享!
更多推荐



所有评论(0)