如何将args和kwargs规范化为kwargs标准形式?
Solution to Parameter Canonicalization
To solve this problem, we'll use Python's inspect module to analyze the function's signature, bind input arguments to that signature, and separate parameters into positional arguments (for required parameters without defaults) and keyword arguments (for parameters with non-default values plus extra keyword args). We'll use the is operator to check if a parameter's value matches its default, omitting it from keyword arguments if so.
Step-by-Step Approach
- Retrieve the function signature: Use
inspect.signature()to get a detailed description of the function's parameters. - Bind input arguments: Map the provided positional and keyword arguments to the function's parameters using
signature.bind(), which handles validation and applies defaults for missing optional parameters. - Process each parameter type:
- Required parameters (no default): Keep their values in positional arguments.
- Optional parameters: Add to keyword arguments only if the value isn't the exact default object (checked via
is). - Variadic positional args (
*args): Extend the positional arguments tuple with these values. - Variadic keyword args (
**kwargs): Merge extra keyword arguments into the result dict.
Complete Implementation
import inspect def canonicalize(func, *args, **kwargs): sig = inspect.signature(func) # Bind input args to the signature, raising TypeError if required args are missing bound_args = sig.bind(*args, **kwargs) # Fill in default values for any missing optional parameters bound_args.apply_defaults() new_args = [] new_kwargs = {} for param_name, param in sig.parameters.items(): value = bound_args.arguments[param_name] if param.kind in (inspect.Parameter.POSITIONAL_ONLY, inspect.Parameter.POSITIONAL_OR_KEYWORD): if param.default is inspect.Parameter.empty: # Required parameter with no default: retain as positional arg new_args.append(value) else: # Optional parameter: add to kwargs only if value differs from default (by identity) if value is not param.default: new_kwargs[param_name] = value elif param.kind == inspect.Parameter.VAR_POSITIONAL: # *args: add all variadic positional values to new_args new_args.extend(value) elif param.kind == inspect.Parameter.KEYWORD_ONLY: if param.default is inspect.Parameter.empty: # Required keyword-only parameter: add to kwargs new_kwargs[param_name] = value else: # Optional keyword-only: add to kwargs only if value differs from default if value is not param.default: new_kwargs[param_name] = value elif param.kind == inspect.Parameter.VAR_KEYWORD: # **kwargs: merge extra keyword args into new_kwargs new_kwargs.update(value) return tuple(new_args), new_kwargs # Test with the provided examples def myfunc(a, b=None, **kwargs): pass print(canonicalize(myfunc, 1, 2, g=3)) # Output: (1,), {'b': 2, 'g': 3} print(canonicalize(myfunc, 1)) # Output: (1,), {} print(canonicalize(myfunc, 1, b=2)) # Output: (1,), {'b': 2} print(canonicalize(myfunc, 1, g=3, b=None)) # Output: (1,), {'g': 3}
Key Details
bound_args.apply_defaults(): Ensures optional parameters not provided in input are filled with their default values, allowing us to check for identity matches.- Identity comparison: Using
isinstead of==ensures we only omit values that are the exact same object as the default, aligning with the requirement to use identity checks. - Robust parameter handling: The code supports all Python parameter types, making it work with any function signature.
内容的提问来源于stack exchange,提问作者Hameer Abbasi
相关产品推荐
相关产品推荐

