Inspect
Python 标准库 inspect 是**“运行时自省(introspection)”**的瑞士军刀。
它把解释器内部对象(模块、类、函数、方法、代码对象、协程、帧、回溯、生成器等)拆成“乐高积木”
- 提取签名、注解、源码、字节码、局部变量
- 判断对象种类(可调用?协程?内置?绑定方法?)
- 遍历类/模块成员并按类型过滤
- 解析堆栈帧与回溯
- 实现装饰器、RPC、序列化、调试器、IDE、ORM、测试框架等高级功能
类别 | 关键函数/类 | 说明 |
---|---|---|
Kind check | ismodule , isclass , isfunction , ismethod , isbuiltin , ismethoddescriptor , isdatadescriptor , isgenerator , iscoroutine , isasyncgen , isframe , istraceback , iscode … | 精确区分对象种类 |
Signature & annotation | signature() , Signature , Parameter , getfullargspec() , get_annotations() | 提取调用签名、默认值、注解、PEP 570 positional-only |
Source & bytecode | getsource() , getsourcelines() , getsourcefile() , getfile() , getcomments() , getclosurevars() , getframeinfo() | 拿到源码、行号、闭包变量 |
Member enumeration | getmembers() , getmembers_static() , getmodulename() , getmoduleinfo() , getclasstree() | 遍历成员并按类型过滤 |
Stack & traceback | currentframe() , stack() , trace() | 调试、日志、Profiler |
Code object 细节 | getargvalues() , formatargvalues() , getargvalues() | 低层字节码操作 |
Import hooks | getmodule() , findsource() , getabsfile() | 与 importlib 配合 |
原理
inspect 模块的核心基于 Python 的 数据模型 和 执行模型:
- 函数/类对象:利用
__annotations__
、__code__
等特殊属性 - 栈帧(Frame):通过
sys._getframe()
获取当前执行上下文 - 签名系统:解析函数的
__code__.co_varnames
和__defaults__
# 手动实现获取函数参数名(简化版)
def get_args(func):
code = func.__code__
return code.co_varnames[:code.co_argcount]
def example(a, b=1): pass
print(get_args(example)) # 输出: ('a', 'b')
API使用
类型检查 (Kind Check)
import inspect
import math
from collections import defaultdict
def example_function():
pass
class ExampleClass:
def instance_method(self):
pass
@classmethod
def class_method(cls):
pass
@staticmethod
def static_method():
pass
# 各种类型检查
print("ismodule(math):", inspect.ismodule(math)) # True
print("isclass(ExampleClass):", inspect.isclass(ExampleClass)) # True
print("isfunction(example_function):", inspect.isfunction(example_function)) # True
print("ismethod(ExampleClass().instance_method):",
inspect.ismethod(ExampleClass().instance_method)) # True
print("isbuiltin(len):", inspect.isbuiltin(len)) # True
print("ismethoddescriptor(str.lower):",
inspect.ismethoddescriptor(str.lower)) # True
print("isdatadescriptor(defaultdict.default_factory):",
inspect.isdatadescriptor(defaultdict.default_factory)) # True
# 生成器与协程检查
def generator_func():
yield 1
async def async_func():
await asyncio.sleep(0)
print("isgeneratorfunction(generator_func):",
inspect.isgeneratorfunction(generator_func)) # True
print("iscoroutinefunction(async_func):",
inspect.iscoroutinefunction(async_func)) # True
# 代码对象检查
code_obj = example_function.__code__
print("iscode(code_obj):", inspect.iscode(code_obj)) # True
签名与注解
def complex_function(
pos_arg,
/,
pos_or_kw: int,
*args: float,
kw_arg: str = "default",
**kwargs: dict
) -> bool:
"""示例函数"""
return True
# 获取函数签名
sig = inspect.signature(complex_function)
print("Signature:", sig)
# (pos_arg, /, pos_or_kw: int, *args: float, kw_arg: str = 'default', **kwargs: dict) -> bool
# 分析参数
for name, param in sig.parameters.items():
print(f"\nParameter: {name}")
print(f" Kind: {param.kind}")
print(f" Default: {param.default if param.default is not param.empty else 'No default'}")
print(f" Annotation: {param.annotation if param.annotation is not param.empty else 'No annotation'}")
# 获取返回值注解
print("\nReturn annotation:", sig.return_annotation) # <class 'bool'>
# 旧版获取参数规范 (Python 3.5+ 推荐使用 signature)
argspec = inspect.getfullargspec(complex_function)
print("\nArgspec:", argspec)
# FullArgSpec(args=['pos_arg', 'pos_or_kw'], varargs='args', varkw='kwargs',
# defaults=('default',), kwonlyargs=[], kwonlydefaults=None, annotations=...)
源代码操作
def sample_function(x, y):
"""计算两个数的和"""
result = x + y
return result
# 获取源代码
source = inspect.getsource(sample_function)
print("Source code:\n", source)
# 获取源代码行
lines, start_line = inspect.getsourcelines(sample_function)
print(f"\nSource lines (starting at line {start_line}):")
for i, line in enumerate(lines, start_line):
print(f"{i}: {line.rstrip()}")
# 获取文件信息
print("\nSource file:", inspect.getsourcefile(sample_function))
print("Module file:", inspect.getfile(inspect))
# 获取注释
print("\nComments:", inspect.getcomments(sample_function))
# 闭包变量检查
def outer_func(x):
def inner_func(y):
return x + y
return inner_func
closure_func = outer_func(10)
closure_vars = inspect.getclosurevars(closure_func)
print("\nClosure variables:", closure_vars)
# ClosureVars(nonlocals={'x': 10}, globals={}, builtins={}, unbound=set())
# 获取栈帧信息
def get_frame_info():
frame = inspect.currentframe()
frame_info = inspect.getframeinfo(frame)
return frame_info
frame_info = get_frame_info()
print("\nFrame info:")
print(f"File: {frame_info.filename}")
print(f"Line: {frame_info.lineno}")
print(f"Function: {frame_info.function}")
print(f"Code context: {frame_info.code_context}")
成员枚举
class ExampleClass:
class_attr = 42
def __init__(self):
self.instance_attr = 100
def public_method(self):
pass
def _protected_method(self):
pass
def __private_method(self):
pass
@classmethod
def cls_method(cls):
pass
@staticmethod
def static_method():
pass
# 获取所有成员
all_members = inspect.getmembers(ExampleClass)
print("\nAll members:")
for name, member in all_members:
print(f"{name}: {type(member).__name__}")
# 过滤特定类型的成员
print("\nMethods:")
methods = inspect.getmembers(ExampleClass, inspect.ismethod)
for name, method in methods:
print(name)
print("\nFunctions:")
functions = inspect.getmembers(ExampleClass, inspect.isfunction)
for name, func in functions:
print(name)
# 静态获取成员(避免触发描述符)
print("\nStatic members:")
static_members = inspect.getmembers_static(ExampleClass)
for name, member in static_members:
print(f"{name}: {type(member).__name__}")
# 获取类层次结构
class Base: pass
class Child(Base): pass
class GrandChild(Child): pass
class_tree = inspect.getclasstree([GrandChild])
print("\nClass tree:")
print(class_tree)
栈与回溯
import traceback
def level_3():
# 获取当前栈帧
current_frame = inspect.currentframe()
print("\nCurrent frame:", current_frame)
# 获取调用栈
stack = inspect.stack()
print("\nCall stack:")
for frame_info in stack:
print(f"Function: {frame_info.function}, Line: {frame_info.lineno}, File: {frame_info.filename}")
# 获取外部帧
outer_frame = current_frame.f_back
outer_frame_info = inspect.getframeinfo(outer_frame)
print("\nOuter frame info:")
print(f"Function: {outer_frame_info.function}")
print(f"Line: {outer_frame_info.lineno}")
print(f"Code: {outer_frame_info.code_context[0].strip() if outer_frame_info.code_context else None}")
def level_2():
level_3()
def level_1():
level_2()
# 生成调用栈
level_1()
# 回溯信息
try:
1 / 0
except ZeroDivisionError:
tb = inspect.trace()
print("\nTraceback:")
for frame_info in tb:
print(f"File {frame_info.filename}, line {frame_info.lineno}, in {frame_info.function}")
print(f" {frame_info.code_context[0].strip() if frame_info.code_context else ''}")
代码对象细节
def sample_func(a, b=2, *args, **kwargs):
x = 10
y = 20
return a + b
# 获取代码对象
code_obj = sample_func.__code__
# 获取参数信息
arg_info = inspect.getargvalues(inspect.currentframe())
print("\nArgument values:", arg_info)
# ArgInfo(args=['a', 'b'], varargs='args', keywords='kwargs', locals={'a': ..., 'b': ...})
# 格式化参数值
formatted_args = inspect.formatargvalues(*inspect.getargvalues(inspect.currentframe()))
print("\nFormatted arguments:", formatted_args)
# 分析代码对象属性
print("\nCode object attributes:")
print(f"co_name: {code_obj.co_name}") # 函数名
print(f"co_argcount: {code_obj.co_argcount}") # 位置参数数量
print(f"co_posonlyargcount: {code_obj.co_posonlyargcount}") # 仅位置参数数量
print(f"co_kwonlyargcount: {code_obj.co_kwonlyargcount}") # 仅关键字参数数量
print(f"co_nlocals: {code_obj.co_nlocals}") # 局部变量数量
print(f"co_stacksize: {code_obj.co_stacksize}") # 所需栈空间
print(f"co_flags: {code_obj.co_flags}") # 标志位
print(f"co_code: {code_obj.co_code[:20]}...") # 字节码
导入钩子
# 获取模块信息
module = inspect.getmodule(inspect)
print("\nModule of inspect:", module) # <module 'inspect' from ...>
# 查找模块源文件
source_file = inspect.getsourcefile(inspect)
print("\nSource file of inspect:", source_file)
# 获取模块绝对路径
abs_file = inspect.getabsfile(inspect)
print("\nAbsolute file path:", abs_file)
# 获取模块名
module_name = inspect.getmodulename(__file__)
print("\nCurrent module name:", module_name)
# 查找模块源文件(旧版)
try:
module_info = inspect.getmoduleinfo(inspect.__file__)
print("\nModule info:", module_info)
except TypeError:
print("\ngetmoduleinfo is deprecated and removed in Python 3.13")
场景
综合案例
import inspect, json, functools
def expose(fn):
"""装饰器:把函数签名暴露为 RPC 描述"""
sig = inspect.signature(fn)
params = [(p.name, str(p.annotation) if p.annotation != inspect.Parameter.empty else 'Any')
for p in sig.parameters.values()]
fn._rpc = {'name': fn.__name__, 'params': params, 'return': str(sig.return_annotation)}
return fn
class Service:
@expose
def add(self, x: int, y: int) -> int:
return x + y
# 运行时自省
for name, method in inspect.getmembers(Service, inspect.isfunction):
print(method._rpc)
# {'name': 'add', 'params': [('x', "<class 'int'>"), ('y', "<class 'int'>")], 'return': "<class 'int'>"}
函数装饰器增强
def log_call(func):
sig = inspect.signature(func)
def wrapper(*args, **kwargs):
bound = sig.bind(*args, **kwargs)
args_str = ", ".join(f"{k}={v}" for k, v in bound.arguments.items())
print(f"调用 {func.__name__}({args_str})")
return func(*args, **kwargs)
return wrapper
@log_call
def multiply(x: float, y: float) -> float:
"""计算两数乘积"""
return x * y
multiply(3, y=4)
# 输出: 调用 multiply(x=3, y=4)
# 返回: 12
动态参数验证器
def validate_arguments(func, *args, **kwargs):
"""验证函数参数是否符合签名要求"""
try:
sig = inspect.signature(func)
bound_args = sig.bind(*args, **kwargs)
bound_args.apply_defaults()
print("✅ Arguments valid!")
return True
except TypeError as e:
print(f"❌ Argument error: {e}")
return False
def example(a: int, b: float = 3.14) -> float:
return a * b
validate_arguments(example, 5) # ✅ Arguments valid!
validate_arguments(example, "five") # ❌ Argument error: ...
validate_arguments(example, 5, 2.0) # ✅ Arguments valid!
validate_arguments(example, 5, b=2.0) # ✅ Arguments valid!
自动文档生成器
def generate_docs(func):
"""自动生成函数文档"""
sig = inspect.signature(func)
doc = {
"name": func.__name__,
"signature": str(sig),
"docstring": inspect.getdoc(func) or "No documentation",
"parameters": [],
"return": sig.return_annotation if sig.return_annotation is not sig.empty else "None"
}
for name, param in sig.parameters.items():
param_info = {
"name": name,
"kind": param.kind.name,
"default": param.default if param.default is not param.empty else None,
"annotation": param.annotation if param.annotation is not param.empty else "Any"
}
doc["parameters"].append(param_info)
return doc
def sample_func(a: int, b: float = 1.0) -> float:
"""计算两个数的乘积
Args:
a: 第一个整数
b: 第二个浮点数,默认为1.0
Returns:
两数的乘积
"""
return a * b
print("\nAuto-generated documentation:")
print(generate_docs(sample_func))
对象序列化器
def serialize_object(obj):
"""序列化Python对象为可JSON化的结构"""
if inspect.ismodule(obj):
return {"type": "module", "name": obj.__name__}
if inspect.isclass(obj):
return {
"type": "class",
"name": obj.__name__,
"module": obj.__module__,
"bases": [base.__name__ for base in obj.__bases__]
}
if inspect.isfunction(obj) or inspect.ismethod(obj):
return {
"type": "function",
"name": obj.__name__,
"module": obj.__module__,
"signature": str(inspect.signature(obj))
}
if isinstance(obj, (int, float, str, bool, type(None))):
return obj
if isinstance(obj, (list, tuple)):
return [serialize_object(item) for item in obj]
if isinstance(obj, dict):
return {key: serialize_object(value) for key, value in obj.items()}
return str(obj)
# 测试序列化
class SampleClass:
def sample_method(self, x):
return x * 2
data = {
"module": math,
"class": SampleClass,
"function": serialize_object,
"method": SampleClass().sample_method,
"value": 42,
"list": [1, 2, 3]
}
print("\nSerialized object:")
import pprint
pprint.pprint(serialize_object(data))
最佳实践
性能考虑
- inspect.stack() 和 inspect.currentframe() 开销较大,避免在性能关键路径中使用
- 对于频繁调用的函数,缓存 inspect.signature() 结果
环境限制
- 在压缩/混淆的代码中无法获取源码
- 动态生成的代码(如 exec() 创建)支持有限
- C 扩展函数(如内置函数)无法获取完整签名
替代方案
- 对于简单类型检查,优先使用 isinstance() 和 issubclass()
- 对于参数验证,考虑使用 typing 模块和类型检查器
- 对于文档生成,使用 Sphinx 等专业工具
版本兼容
- inspect.getargspec() 在 Python 3.5+ 中已弃用,使用 inspect.signature() 替代
- inspect.getmoduleinfo() 在 Python 3.13+ 中已移除
安全考虑
- 避免在生产环境暴露源码获取功能
- 谨慎处理来自不可信源的函数签名
← Previous postSecurity夯实基础
Next post →Python框架Flask