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

如何更简洁安全地获取函数传入参数并区分关键字参数是否显式传入?

问题描述

我需要编写一个函数,内部逻辑要区分调用时显式传入的可选关键字参数和使用默认值的参数,分别执行不同逻辑。目前已有可行实现,但使用*args+**kwargs导致函数签名不明确,想找更简洁、安全、紧凑的写法。

现有实现代码

import json

def addFeature(*args,**kwargs):
    defaults={
        'p1':1,
        'p2':2,
        'p3':3
    }
    mergedKwargs = defaults | kwargs # 先取默认值,再用传入的kwargs覆盖
    print(f'args:{args}')
    print('调用时显式传入的关键字参数:')
    print(json.dumps(kwargs,indent=3))
    print('合并后的参数:')
    print(json.dumps(mergedKwargs,indent=3))
    # 解包到局部变量
    (p1,p2,p3)=(mergedKwargs[k] for k in ('p1','p2','p3'))
    print(f'局部变量值: p1={p1}  p2={p2}  p3={p3}')

addFeature(1,2,p1=5)
addFeature(3,4,p2=7,p3=9)

现有代码运行结果

PS C:\Users\caver\Documents\GitHub> python argsTest.py
args: (1, 2)
调用时显式传入的关键字参数:
{
   "p1": 5
}
合并后的参数:
{
   "p1": 5,
   "p2": 2,
   "p3": 3
}
局部变量值: p1=5  p2=2  p3=3

args: (3, 4)
调用时显式传入的关键字参数:
{
   "p2": 7,
   "p3": 9
}
合并后的参数:
{
   "p1": 1,
   "p2": 7,
   "p3": 9
}
局部变量值: p1=1  p2=7  p3=9

PS C:\Users\caver\Documents\GitHub>

优化方案

方案1:显式定义参数 + inspect模块追踪传入状态

这种写法让函数签名完全清晰,IDE能自动补全参数,同时通过inspect模块准确判断哪些参数是用户显式传入的:

import json
import inspect

def addFeature(*args, p1=1, p2=2, p3=3):
    # 获取函数签名和调用时的参数绑定关系
    sig = inspect.signature(addFeature)
    bound_args = sig.bind(*args, p1=p1, p2=p2, p3=p3)
    bound_args.apply_defaults()
    
    # 筛选出显式传入的关键字参数
    explicit_kwargs = {}
    for name, param in sig.parameters.items():
        if param.kind == param.KEYWORD_ONLY and name in bound_args.arguments:
            # 对比参数值和默认值,判断是否为显式传入
            if bound_args.arguments[name] is not param.default:
                explicit_kwargs[name] = bound_args.arguments[name]
    
    print(f'args:{args}')
    print('调用时显式传入的关键字参数:')
    print(json.dumps(explicit_kwargs, indent=3))
    print('合并后的参数:')
    print(json.dumps({'p1': p1, 'p2': p2, 'p3': p3}, indent=3))
    print(f'局部变量值: p1={p1}  p2={p2}  p3={p3}')

addFeature(1,2,p1=5)
addFeature(3,4,p2=7,p3=9)

优势:参数签名明确,类型提示友好,能精准区分显式传入/默认值参数,适合需要严格参数校验的场景。

方案2:**kwargs+类型提示+参数校验(兼顾简洁性)

如果不想引入inspect模块,可以保留**kwargs但通过类型提示明确允许的参数,同时添加参数校验避免无效输入:

import json
from typing import Optional, Dict

def addFeature(*args, **kwargs: Optional[int]):
    defaults: Dict[str, int] = {'p1':1, 'p2':2, 'p3':3}
    # 校验传入的参数是否在允许范围内
    invalid_args = set(kwargs.keys()) - set(defaults.keys())
    if invalid_args:
        raise ValueError(f"不支持的关键字参数: {invalid_args}")
    
    mergedKwargs = defaults | kwargs
    explicit_kwargs = kwargs.copy()
    
    print(f'args:{args}')
    print('调用时显式传入的关键字参数:')
    print(json.dumps(explicit_kwargs, indent=3))
    print('合并后的参数:')
    print(json.dumps(mergedKwargs, indent=3))
    p1, p2, p3 = mergedKwargs['p1'], mergedKwargs['p2'], mergedKwargs['p3']
    print(f'局部变量值: p1={p1}  p2={p2}  p3={p3}')

addFeature(1,2,p1=5)
addFeature(3,4,p2=7,p3=9)

优势:代码简洁,保留了**kwargs的灵活性,同时通过类型提示和校验保证安全性,适合参数较少的场景。

方案3:用dataclasses结构化管理参数

如果参数较多或需要结构化管理,用dataclasses能让参数定义更清晰,同时轻松追踪显式传入的参数:

import json
from dataclasses import dataclass, fields

@dataclass
class FeatureParams:
    p1: int = 1
    p2: int = 2
    p3: int = 3

def addFeature(*args, **kwargs):
    # 初始化参数实例,自动处理默认值
    params = FeatureParams(**kwargs)
    # 收集显式传入的参数
    explicit_kwargs = {field.name: kwargs[field.name] for field in fields(params) if field.name in kwargs}
    
    print(f'args:{args}')
    print('调用时显式传入的关键字参数:')
    print(json.dumps(explicit_kwargs, indent=3))
    print('合并后的参数:')
    print(json.dumps({'p1': params.p1, 'p2': params.p2, 'p3': params.p3}, indent=3))
    print(f'局部变量值: p1={params.p1}  p2={params.p2}  p3={params.p3}')

addFeature(1,2,p1=5)
addFeature(3,4,p2=7,p3=9)

优势:参数结构清晰,扩展性强,后续新增参数只需修改dataclass即可,适合参数较多的复杂场景。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 02:44:52