You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从字符串还原带泛型的Python函数参数类型与默认值?

问题描述

我通过inspect模块获取函数参数的注解与默认值,将其序列化为字符串形式的字典,代码如下:

import inspect, importlib
from pydoc import locate

def func(var:str = 'test', var2: int = 1, var3:list[int] = [1,2,3]):
    pass

fspec = inspect.getfullargspec(func)
dflts = fspec.defaults
spec_dict = {}
for i, (a) in enumerate(fspec.annotations.items()):
    type_name = a[1].__name__ if type(a[1]) is type else str(a[1])
    spec_dict[a[0]] =(str(dflts[i]), str(type_name))
print(spec_dict)

生成的字典:

{'var': ('test', 'str'), 'var2': ('1', 'int'), 'var3': ('[1, 2, 3]', 'list[int]')}

之后尝试将字符串值转回对应类型,编写了如下代码:

for v in spec_dict.values():
    val_as_str = v[0]
    param_type = locate(v[1])
    print(param_type(val_as_str))

但处理list[int]时,pydoc.locate返回None导致失败。由于需要通过JSON序列化反序列化实现函数签名的还原,求正确的处理方式。


解决方案

1. 正确获取类型注解:用typing.get_type_hints替代inspect.getfullargspec

inspect.getfullargspec获取的泛型注解(如list[int])只是字符串形式,而typing.get_type_hints能拿到真实的类型对象,方便后续序列化时保存准确的类型结构信息。

修改获取注解与默认值的代码:

import inspect
from typing import get_type_hints

def func(var:str = 'test', var2: int = 1, var3:list[int] = [1,2,3]):
    pass

type_hints = get_type_hints(func)
fspec = inspect.getfullargspec(func)
dflts = fspec.defaults

spec_dict = {}
# 匹配参数与对应默认值(默认值顺序为从最后一个参数开始)
args_with_defaults = fspec.args[-len(dflts):] if dflts else []
for arg, default in zip(args_with_defaults, dflts):
    param_type = type_hints[arg]
    # 序列化类型:区分普通类型与泛型类型
    if hasattr(param_type, '__origin__'):
        type_info = {
            'origin': param_type.__origin__.__name__,
            'args': [t.__name__ if hasattr(t, '__name__') else str(t) for t in param_type.__args__]
        }
    else:
        type_info = {'type': param_type.__name__}
    # 用repr()序列化默认值,保证能被安全解析
    spec_dict[arg] = (repr(default), type_info)

print(spec_dict)

生成的字典会包含泛型的结构信息,比如var3的类型信息为{'origin': 'list', 'args': ['int']},而非字符串list[int]。

2. 反序列化时还原类型与默认值

  • 用ast.literal_eval解析默认值字符串,比eval更安全,能正确还原列表、字典等复杂结构。
  • 对于泛型类型,通过typing模块的内置对象构造;对于普通类型,通过模块导入获取。
  • 支持递归处理嵌套泛型(如list[dict[str, int]])。

反序列化代码示例:

import ast
from typing import *

def restore_type(type_info):
    if 'type' in type_info:
        # 处理普通类型,支持自定义模块类型
        if '.' in type_info['type']:
            module_name, type_name = type_info['type'].split('.', 1)
        else:
            module_name, type_name = 'builtins', type_info['type']
        module = __import__(module_name)
        return getattr(module, type_name)
    elif 'origin' in type_info:
        # 处理泛型类型
        origin = typing.__dict__.get(type_info['origin'])
        args = [restore_type({'type': arg}) for arg in type_info['args']]
        return origin[tuple(args)]

def restore_default(val_str):
    return ast.literal_eval(val_str)

# 反序列化示例
for arg, (val_str, type_info) in spec_dict.items():
    param_type = restore_type(type_info)
    default_val = restore_default(val_str)
    print(f"{arg}: {default_val} (类型: {param_type}, 实际类型: {type(default_val)})")

3. 适配JSON序列化

JSON仅支持基本类型,上述代码生成的type_info为字典结构,可直接序列化。序列化与反序列化示例:

import json

# 序列化到JSON文件
json_str = json.dumps(spec_dict)
with open('func_spec.json', 'w') as f:
    f.write(json_str)

# 从JSON文件反序列化
with open('func_spec.json', 'r') as f:
    loaded_spec = json.load(f)

# 还原类型与默认值
for arg, (val_str, type_info) in loaded_spec.items():
    param_type = restore_type(type_info)
    default_val = restore_default(val_str)
    print(f"{arg}: {default_val} (类型: {param_type})")

注意事项

  • 必须用repr()而非str()处理默认值,确保字符串、列表等结构的表示能被ast.literal_eval正确解析。
  • 自定义类型需保证所在模块能被导入,可扩展restore_type函数处理自定义模块路径。
  • 对于Union[int, str]这类特殊泛型,需在restore_type中增加对应判断逻辑,构造正确的联合类型。

内容的提问来源于stack exchange,提问作者mike01010

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 10:05:23