文章目录

Python inspect模块动态获取函数签名与参数默认值

发布于 2026-04-29 07:13:46 · 浏览 6 次 · 评论 0 条

Python inspect模块动态获取函数签名与参数默认值

Python 内置的 inspect 模块能够让我们在程序运行时“偷看”函数的内部结构,无需手动查阅源代码。这在编写装饰器、API 文档生成工具或动态调用函数时非常有用。


1. 准备一个演示用的目标函数

首先,我们需要一个包含多种参数类型(位置参数、关键字参数、默认值、类型注解)的函数作为分析对象。

编写以下代码并保存:

def process_data(user_id, name="Anonymous", *, role="guest", active=True):
    """
    模拟一个数据处理函数。
    :param user_id: 用户ID
    :param name: 用户名,默认为 Anonymous
    :param role: 用户角色,仅限关键字参数
    :param active: 是否激活,默认为 True
    """
    print(f"Processing {user_id}: {name} ({role}, active={active})")

2. 获取函数的签名对象

inspect.signature 是核心入口,它将函数的可调用信息封装为一个 Signature 对象。

导入 inspect 模块,并调用 signature 方法:

import inspect

# 获取函数签名
sig = inspect.signature(process_data)

# 打印签名概览
print(f"函数签名: {sig}")
# 输出示例: (user_id, name='Anonymous', *, role='guest', active=True)

解析 sig 对象,你会发现它包含了参数列表和返回值注解。


3. 遍历并提取参数详细信息

Signature 对象的 parameters 属性是一个有序映射(类似字典),存储了所有的参数信息。我们可以遍历它来获取每一个参数的详情。

编写如下循环代码来提取参数名、默认值和类型:

print("\n--- 参数详细解析 ---")

for param_name, param_obj in sig.parameters.items():
    # 获取参数名称
    name = param_obj.name

    # 获取参数默认值
    # 注意:如果参数没有默认值,其 default 属性是 inspect.Parameter.empty
    default = param_obj.default
    default_str = repr(default) if default is not inspect.Parameter.empty else "无默认值"

    # 获取参数类型注解
    annotation = param_obj.annotation
    annotation_str = str(annotation) if annotation is not inspect.Parameter.empty else "无注解"

    # 获取参数种类(POSITIONAL, KEYWORD_ONLY, VAR_POSITIONAL 等)
    kind = param_obj.kind

    print(f"参数名: {name}")
    print(f"  默认值: {default_str}")
    print(f"  类型注解: {annotation_str}")
    print(f"  参数种类: {kind}")
    print("-" * 20)

4. 理解参数属性对照表

在处理 param_obj(即 Parameter 对象)时,你会频繁接触到以下几个属性。下表列出了它们的含义:

属性名 类型/返回值 说明
name str 参数的名称字符串。
default Any 参数的默认值。重点:如果没有默认值,该值为 inspect.Parameter.empty,而不是 None
annotation type 参数的类型提示(如 int, str)。若无注解,值为 inspect.Parameter.empty
kind _ParameterKind 枚举值,描述参数的模式。常见的有 POSITIONAL_OR_KEYWORD(普通参数)、KEYWORD_ONLY* 后的参数)、VAR_POSITIONAL*args)。

5. 实战:构建一个参数检查字典

在很多场景下(如构建动态 SQL 或 HTTP 请求),我们需要知道哪些参数是被用户“省略”了的,从而使用默认值。

运行以下代码,生成一个包含“参数名: 默认值”的字典:

def get_defaults_map(func):
    """提取函数的所有有默认值的参数及其默认值"""
    sig = inspect.signature(func)
    defaults = {}

    for name, param in sig.parameters.items():
        # 仅保存拥有默认值的参数
        if param.default is not inspect.Parameter.empty:
            defaults[name] = param.default

    return defaults

# 测试提取
defaults = get_defaults_map(process_data)
print("默认值字典:", defaults)
# 预期输出: {'name': 'Anonymous', 'role': 'guest', 'active': True}

注意user_id 不会出现在这个字典中,因为它没有默认值。


6. 动态绑定参数调用函数

获取签名的最终目的往往是为了安全地调用函数。我们可以利用 Signature.bind 方法,将一个字典动态映射到函数参数上,Python 会自动处理位置参数和关键字参数的匹配。

执行以下代码模拟动态调用:

# 模拟从外部(如配置文件或HTTP请求)获取的参数数据
input_data = {
    "user_id": 1001,
    "role": "admin"
    # 故意不传 name 和 active,测试是否自动填充默认值
}

try:
    # 将输入数据绑定到函数签名
    bound_args = sig.bind(**input_data)

    # 应用默认值
    # bind() 方法如果遇到缺失参数会报错,除非该参数有默认值
    # 但 bind 默认行为是:提供的参数必须足够填充无默认值的位置参数
    # 这里 user_id 已提供,name 虽未提供但在 user_id 之后且为非仅关键字参数?
    # 不,name 是普通参数。bind 严格要求参数完整性。

    # 更好的做法是:先绑定,再补全默认值
    bound_args.apply_defaults()

    print(f"\n最终传递的参数: {bound_args.arguments}")

    # 使用 *bound_args.args, **bound_args.kwargs 调用函数
    process_data(*bound_args.args, **bound_args.kwargs)

except TypeError as e:
    print(f"参数错误: {e}")

关键点
bound_args.apply_defaults()inspect 模块中非常方便的方法。它会自动检查签名,将那些调用方未提供但函数定义了默认值的参数,自动填充到 bound_args.arguments 字典中。


7. 判断参数是否为仅关键字参数

process_data 中,roleactive 位于 * 之后,这意味着它们必须通过关键字传递。我们可以通过检查 param.kind 来识别这一点。

使用枚举比较进行判断:

for name, param in sig.parameters.items():
    if param.kind == inspect.Parameter.KEYWORD_ONLY:
        print(f"'{name}' 是仅关键字参数,必须使用关键字形式调用。")
常用 Kind 常量 含义 示例定义
POSITIONAL_OR_KEYWORD 可以是位置参数或关键字参数 def f(a)
VAR_POSITIONAL 任意数量的位置参数 (*args) def f(*args)
KEYWORD_ONLY 必须是关键字参数 def f(*, a)
VAR_KEYWORD 任意数量的关键字参数 (**kwargs) def f(**kwargs)

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文