inspect
inspect 模块提供了一些有用的函数帮助获取对象的信息,例如模块、类、方法、函数、回溯、帧对象以及代码对象。例如它可以帮助你检查类的内容,获取某个方法的源代码,取得并格式化某个函数的参数列表,或者获取你需要显示的回溯的详细信息。
该模块提供了4种主要的功能:类 型检查、获取源代码、检查类与函数、检查解释器的调用堆栈。
类型检查
getmembers() 函数获取对象如类或模块的成员。名称以 "is" 打头的函数主要用于判断对象类型。
基本类型判断
import inspect
class MyClass:
def method(self):
pass
def my_function():
pass
# 判断对象类型
print(inspect.isclass(MyClass)) # True
print(inspect.ismethod(MyClass().method)) # True
print(inspect.isfunction(my_function)) # True
print(inspect.ismodule(inspect)) # True
print(inspect.isbuiltin(print)) # True
获取对象成员
import inspect
class Person:
"""人员类"""
name = "张三"
def __init__(self, age):
self.age = age
@staticmethod
def a():
pass
@classmethod
def b(cls):
pass
def c(self):
pass
# 获取类的所有成员(属性、方法)
members = inspect.getmembers(Person)
for name, value in members:
# 排除魔法方法
if not name.startswith('__'):
print(f"{name}: {value}")
"""
a: <function Person.a at 0x00000250F315B9C0>
b: <bound method Person.b of <class '__main__.Person'>>
c: <function Person.c at 0x00000250F315BA60>
name: 张三
"""
# 只获取类方法
methods = inspect.getmembers(Person, predicate=inspect.ismethod)
print(methods)
"""
[('b', <bound method Person.b of <class '__main__.Person'>>)]
"""
# 只获取函数(包括未绑定的方法)
functions = inspect.getmembers(Person, predicate=inspect.isfunction)
print(functions)
"""
[('__init__', <function Person.__init__ at 0x000001976EF48D60>),
('a', <function Person.a at 0x000001976F0BB9C0>),
('c', <function Person.c at 0x000001976F0BBA60>)
]
"""
常用类型检查函数
import inspect
import types
def example():
pass
class Example:
def method(self):
pass
# 检查是否为生成器
def gen():
yield 1
print(inspect.isgeneratorfunction(gen)) # True
print(inspect.isgenerator(gen())) # True
# 检查是否为协程
import asyncio
async def coro():
await asyncio.sleep(1)
print(inspect.iscoroutinefunction(coro)) # True
print(inspect.iscoroutine(coro())) # True
# 检查是否为抽象基类
import abc
class Abstract(abc.ABC):
@abc.abstractmethod
def method(self):
pass
print(inspect.isabstract(Abstract)) # True
获取源代码
获取函数和类的源代码
import inspect
def foo(x: int, y: int) -> int:
"""计算两数之和"""
return x + y
# 获取源代码
print(inspect.getsource(foo))
# 输出:
# def foo(x: int, y: int) -> int:
# """计算两数之和"""
# return x + y
# 获取源代码行号范围
lines = inspect.getsourcelines(foo)
print(f"行号: {lines[1]}, 总行数: {len(lines[0])}")
# 行号: 4, 总行数: 3
# 获取文件位置
print(inspect.getfile(foo)) # 文件路径
# d:\test\test.py
print(inspect.getmodule(foo)) # 模块对象
# <module '__main__' from 'd:\\test\\test.py'>
获取文档字符串
import inspect
def documented_function():
"""这是一个有文档的函数
这是详细说明。
"""
pass
print(inspect.getdoc(documented_function))
# 输出: 这是一个有文档的函数\n\n这是详细说明。
获取源代码的限制
getsource() 只能获取在 Python 源代码中定义的对象的源代码。对于:
- C 扩展模块中的函数
- 内置函数
- 动态生成的代码
将无法获取源代码,会抛出 OSError。
使用 Signature 对象进行内省
Signature 对象提供了函数签名的详细信息,包括参数名称、默认值、类型注解等。
import inspect
def example(a: int, b: str = "default", *args, **kwargs) -> str:
"""示例函数"""
return f"{a}-{b}"
# 获取函数签名
sig = inspect.signature(example)
print(sig)
# 输出: (a: int, b: str = 'default', *args, **kwargs) -> str
# 获取参数信息
for name, param in sig.parameters.items():
print(f"{name}: {param.annotation} = {param.default}")
print(f" kind: {param.kind}")
print(f" required: {param.default is inspect.Parameter.empty}")
# 参数类型
def example(pos, /, pos_or_kw, *, kw_only):
pass
sig = inspect.signature(example)
for name, param in sig.parameters.items():
kind = param.kind
if kind == inspect.Parameter.POSITIONAL_ONLY:
print(f"{name}: 仅位置参数")
elif kind == inspect.Parameter.POSITIONAL_OR_KEYWORD:
print(f"{name}: 位置或关键字参数")
elif kind == inspect.Parameter.VAR_POSITIONAL:
print(f"{name}: *args")
elif kind == inspect.Parameter.VAR_KEYWORD:
print(f"{name}: **kwargs")
elif kind == inspect.Parameter.KEYWORD_ONLY:
print(f"{name}: 仅关键字参数")
# 绑定参数
def func(a, b=10, *args, c=20, **kwargs):
return a, b, args, c, kwargs
sig = inspect.signature(func)
# 绑定参数
bound = sig.bind(1, 2, 3, 4, c=30, d=40)
print(bound.arguments) # {'a': 1, 'b': 2, 'args': (3, 4), 'c': 30, 'kwargs': {'d': 40}}
# 应用默认值
bound.apply_defaults()
print(bound.arguments)
# {'a': 1, 'b': 2, 'args': (3, 4), 'c': 30, 'd': 40}
检查类与函数
获取函数参数信息
import inspect
def example(a, b=10, *args, c=20, **kwargs):
"""示例函数"""
pass
# 获取完整参数信息(推荐)
full_args = inspect.getfullargspec(example)
print(full_args)
# FullArgSpec(args=['a', 'b'], varargs='args', varkw='kwargs',
# defaults=(10,), kwonlyargs=['c'], kwonlydefaults={'c': 20},
# annotations={})
# 获取参数名称
print(full_args.args) # ['a', 'b']
print(full_args.varargs) # 'args'
print(full_args.varkw) # 'kwargs'
print(full_args.defaults) # (10,)
print(full_args.kwonlyargs) # ['c']
print(full_args.kwonlydefaults) # {'c': 20}
已弃用的函数
getargspec() 在 Python 3.11 中已弃用,应使用 getfullargspec() 或 signature()。
检查类层次结构
import inspect
class A:
pass
class B(A):
pass
class C(B):
pass
class D(A):
pass
# 获取类层次结构
tree = inspect.getclasstree([A, B, C, D])
print(tree)
# 输出: [(<class '__main__.A'>, ()), [(<class '__main__.B'>, (<class '__main__.A'>,)),
# [(<class '__main__.C'>, (<class '__main__.B'>,))],
# (<class '__main__.D'>, (<class '__main__.A'>,))]]
# 获取方法解析顺序(MRO)
print(C.__mro__) # 或使用 inspect.getmro(C)
获取调用者信息
import inspect
def caller_info():
"""获取调用者信息"""
frame = inspect.currentframe()
caller_frame = frame.f_back
return {
'filename': caller_frame.f_code.co_filename,
'lineno': caller_frame.f_lineno,
'function': caller_frame.f_code.co_name
}
def test():
info = caller_info()
print(f"在 {info['filename']} 的第 {info['lineno']} 行调用")
test()
检查解释器的调用堆栈
获取调用栈
import inspect
def level3():
stack = inspect.stack()
print("调用栈:")
for frame_info in stack:
print(f" {frame_info.function} in {frame_info.filename}:{frame_info.lineno}")
def level2():
level3()
def level1():
level2()
level1()
# 输出:
# 调用栈:
# level3 in ...:5
# level2 in ...:11
# level1 in ...:14
# <module> in ...:17
获取当前帧信息
import inspect
def example():
# 获取当前帧
frame = inspect.currentframe()
print(f"文件名: {frame.f_code.co_filename}")
print(f"函数名: {frame.f_code.co_name}")
print(f"行号: {frame.f_lineno}")
# 获取局部变量
print(f"局部变量: {frame.f_locals}")
def test(x=10):
y = 20
example()
test(100)
获取回溯信息
import inspect
import traceback
def func1():
func2()
def func2():
func3()
def func3():
# 获取当前异常的回溯
try:
raise ValueError("测试异常")
except:
tb = inspect.trace()
print("回溯信息:")
for frame_info in tb:
print(f" {frame_info.function} in {frame_info.filename}:{frame_info.lineno}")
func1()
静态地获取属性
getattr_static() 可以获取属性而不触发描述器协议或 __getattr__() 的执行。
import inspect
class Descriptor:
def __get__(self, obj, objtype=None):
print("描述器被调用!")
return 42
class MyClass:
attr = Descriptor()
normal_attr = "正常属性"
obj = MyClass()
# 使用 getattr() 会触发描述器
value1 = getattr(obj, 'attr')
# 输出: 描述器被调用!
# 使用 getattr_static() 不会触发描述器
value2 = inspect.getattr_static(obj, 'attr')
print(type(value2)) # <class '__main__.Descriptor'>
print(value2) # <__main__.Descriptor object at ...>
# 正常属性两者行为相同
print(getattr(obj, 'normal_attr')) # 正常属性
print(inspect.getattr_static(obj, 'normal_attr')) # 正常属性
使用场景
getattr_static() 主要用于文档工具等需要静态内省的场景,避免触发动态属性查找。
检查生成器状态
import inspect
def generator():
yield 1
yield 2
gen = generator()
print(inspect.getgeneratorstate(gen)) # GEN_CREATED
next(gen)
print(inspect.getgeneratorstate(gen)) # GEN_SUSPENDED
try:
next(gen)
next(gen)
except StopIteration:
pass
print(inspect.getgeneratorstate(gen)) # GEN_CLOSED
# 获取生成器的局部变量
def gen_with_locals():
x = 10
yield x
y = 20
yield y
gen2 = gen_with_locals()
next(gen2)
print(inspect.getgeneratorlocals(gen2)) # {'x': 10}
检查协程状态
import inspect
import asyncio
async def coroutine():
await asyncio.sleep(0.1)
return "完成"
coro = coroutine()
print(inspect.getcoroutinestate(coro)) # CORO_CREATED
# 运行协程
asyncio.run(coro)
print(inspect.getcoroutinestate(coro)) # CORO_CLOSED