28 inspect自省
你想知道一个函数有哪些参数、一个类有哪些方法、一段代码的源码是什么——这些在运行时动态检查对象的能力叫"自省"(Introspection)。inspect模块就是干这个的。
一、类型检查
1.1 常用检查函数
python
import inspect
# 检查类型
inspect.isclass(int) # True
inspect.isfunction(lambda: None) # True
inspect.ismethod(str.upper) # True
inspect.ismodule(inspect) # True
# 检查是否为协程函数
async def async_func(): pass
inspect.iscoroutinefunction(async_func) # True
# 检查是否为生成器函数
def gen():
yield 1
inspect.isgeneratorfunction(gen) # True1.2 完整类型检查列表
python
import inspect
obj = "hello"
inspect.isclass(type(obj)) # True
inspect.isfunction(obj) # False
inspect.ismethod(obj) # False
inspect.ismodule(obj) # False
inspect.isbuiltin(len) # True
inspect.isroutine(obj) # False(函数或方法)二、获取成员
2.1 getmembers()
python
import inspect
# 获取对象的所有成员
members = inspect.getmembers(str)
for name, value in members[:5]:
print(f"{name}: {type(value).__name__}")2.2 带过滤条件
python
import inspect
# 只获取方法
methods = inspect.getmembers(str, predicate=inspect.ismethod)
print(methods[:5])
# 只获取函数
functions = inspect.getmembers(inspect, predicate=inspect.isfunction)
print(functions[:5])三、函数签名
3.1 获取签名
python
import inspect
def example(a: int, b: str = "hello", *args, **kwargs) -> bool:
pass
sig = inspect.signature(example)
print(sig) # (a: int, b: str = 'hello', *args, **kwargs) -> bool3.2 分析参数
python
import inspect
def example(a: int, b: str = "hello", *args, c: float = 1.0, **kwargs) -> bool:
pass
sig = inspect.signature(example)
for name, param in sig.parameters.items():
print(f"参数: {name}")
print(f" 类型注解: {param.annotation}")
print(f" 默认值: {param.default}")
print(f" 种类: {param.kind}")参数种类:
| 种类 | 说明 |
|---|---|
POSITIONAL_ONLY | 仅位置参数 |
POSITIONAL_OR_KEYWORD | 位置或关键字参数(默认) |
VAR_POSITIONAL | *args |
KEYWORD_ONLY | 仅关键字参数 |
VAR_KEYWORD | **kwargs |
3.3 绑定参数
python
import inspect
def greet(name, greeting="Hello"):
return f"{greeting}, {name}!"
sig = inspect.signature(greet)
# 绑定参数
bound = sig.bind("World")
print(bound.arguments) # {'name': 'World'}
# 带默认值
bound = sig.bind("World", "Hi")
print(bound.arguments) # {'name': 'World', 'greeting': 'Hi'}
# 关键字参数
bound = sig.bind(name="World", greeting="Hey")
print(bound.arguments) # {'name': 'World', 'greeting': 'Hey'}四、获取源代码
4.1 getsource()
python
import inspect
def hello():
"""打招呼"""
print("Hello, World!")
# 获取源代码
source = inspect.getsource(hello)
print(source)4.2 getsourcelines()
python
import inspect
def hello():
print("Hello")
# 获取源代码和起始行号
lines, lineno = inspect.getsourcelines(hello)
print(f"起始行: {lineno}")
print(f"代码: {lines}")4.3 getfile()
python
import inspect
# 获取定义位置
print(inspect.getfile(inspect)) # /usr/lib/python3.x/inspect.py五、调用栈检查
5.1 stack()
python
import inspect
def a():
b()
def b():
c()
def c():
# 获取调用栈
frame_stack = inspect.stack()
for frame_info in frame_stack:
print(f"函数: {frame_info.function}, 行: {frame_info.lineno}")
a()5.2 currentframe()
python
import inspect
def example():
frame = inspect.currentframe()
print(f"当前函数: {frame.f_code.co_name}")
print(f"调用者: {frame.f_back.f_code.co_name}")
def caller():
example()
caller()5.3 getframeinfo()
python
import inspect
def example():
frame = inspect.currentframe()
info = inspect.getframeinfo(frame)
print(f"文件: {info.filename}")
print(f"行号: {info.lineno}")
print(f"函数: {info.function}")
print(f"代码: {info.code_context}")六、类检查
6.1 获取类的方法
python
import inspect
class MyClass:
def method1(self):
pass
def method2(self):
pass
# 获取所有方法
methods = inspect.getmembers(MyClass, predicate=inspect.isfunction)
for name, func in methods:
print(f"方法: {name}")6.2 获取方法签名
python
import inspect
class Calculator:
def add(self, a: int, b: int) -> int:
return a + b
sig = inspect.signature(Calculator.add)
print(sig) # (self, a: int, b: int) -> int
# 获取参数(排除self)
params = {k: v for k, v in sig.parameters.items() if k != 'self'}
print(params)七、模块检查
7.1 获取模块信息
python
import inspect
# 获取模块成员
members = inspect.getmembers(inspect)
# 获取模块中的类
classes = inspect.getmembers(inspect, inspect.isclass)
print(f"inspect模块有 {len(classes)} 个类")
# 获取模块中的函数
functions = inspect.getmembers(inspect, inspect.isfunction)
print(f"inspect模块有 {len(functions)} 个函数")八、实战场景
8.1 自动文档生成
python
import inspect
def generate_docs(func):
"""生成函数文档"""
sig = inspect.signature(func)
doc = func.__doc__ or "无文档"
lines = [f"函数: {func.__name__}"]
lines.append(f"签名: {sig}")
lines.append(f"文档: {doc}")
lines.append("参数:")
for name, param in sig.parameters.items():
default = f" = {param.default}" if param.default != inspect.Parameter.empty else ""
annotation = f": {param.annotation}" if param.annotation != inspect.Parameter.empty else ""
lines.append(f" {name}{annotation}{default}")
return "\n".join(lines)
def example(a: int, b: str = "hello") -> bool:
"""示例函数"""
return True
print(generate_docs(example))8.2 参数验证
python
import inspect
def validate_args(func, *args, **kwargs):
"""验证参数"""
sig = inspect.signature(func)
try:
sig.bind(*args, **kwargs)
return True
except TypeError as e:
print(f"参数错误: {e}")
return False
def add(a: int, b: int) -> int:
return a + b
validate_args(add, 1, 2) # True
validate_args(add, 1) # False: 缺少参数
validate_args(add, 1, 2, 3) # False: 参数过多8.3 装饰器中获取原函数信息
python
import inspect
def my_decorator(func):
# 保留原函数的签名
sig = inspect.signature(func)
def wrapper(*args, **kwargs):
# 验证参数
sig.bind(*args, **kwargs)
return func(*args, **kwargs)
wrapper.__signature__ = sig
wrapper.__name__ = func.__name__
return wrapper
@my_decorator
def greet(name: str, greeting: str = "Hello") -> str:
return f"{greeting}, {name}!"
# 装饰后的函数保留了原签名
print(inspect.signature(greet)) # (name: str, greeting: str = 'Hello') -> str8.4 调试信息
python
import inspect
def debug_call(func):
def wrapper(*args, **kwargs):
frame = inspect.currentframe().f_back
print(f"调用 {func.__name__}()")
print(f" 来自 {frame.f_code.co_name}:{frame.f_lineno}")
result = func(*args, **kwargs)
print(f" 返回 {result}")
return result
return wrapper
@debug_call
def add(a, b):
return a + b
add(1, 2)九、总结
inspect模块的核心:
| 函数 | 用途 |
|---|---|
isclass/isfunction/ismethod | 类型检查 |
getmembers() | 获取对象成员 |
signature() | 获取函数签名 |
getsource() | 获取源代码 |
stack()/currentframe() | 调用栈检查 |
getframeinfo() | 帧信息 |
使用场景:
- 自动文档生成
- 参数验证
- 调试和日志
- 元编程
- 装饰器中保留原函数信息
inspect是Python元编程的基础,写框架、写装饰器时特别有用。