简体   繁体   English

使用默认参数时如何验证 python 方法的签名?

[英]How to verify the signature of python method when using Default parameters?

Is there a way to check if all the argument passes to a method matches the expected one?有没有办法检查传递给方法的所有参数是否与预期的参数匹配? I tried the inspect.signature.bind to match them but it does now work properly when using a default parameter, even when the type of the default value is not same as passed.我尝试使用 inspect.signature.bind 来匹配它们,但它现在在使用默认参数时可以正常工作,即使默认值的类型与传递的类型不同。 eg in the last call to the method faa, I send a list as the third argument, but the expected default argument is Bool.例如,在最后一次调用faa 方法时,我发送了一个列表作为第三个参数,但预期的默认参数是Bool。 Is there a way to match that also?有没有办法匹配呢?

import inspect
def foo(a, b, x=True):
    pass

def faa(*args, **kwargs):
  try:
    inspect.signature(foo).bind(*args, **kwargs)
  except TypeError:
    print("Does not match")
  else:
    print("Matches")  


faa(1)                     # Does not match , Ok
faa(10, None, 'something') # Matches, Ok.
faa(1, 2)                  # Matches, Ok.
faa(10, None, [1,2])       # Matches, NOK. Is there a way to add a check to not match ?

You could use the decorator validate_arguments belonging to the module pydantic .您可以使用属于模块pydantic的装饰器validate_arguments As an example:举个例子:

from pydantic import validate_arguments


@validate_arguments
def faa(a, b, x: bool = True):
    pass


faa(
    a=10, b=None, x=[1, 2]
)

Output Output

pydantic.error_wrappers.ValidationError: 1 validation error for Faa
x
  value could not be parsed to a boolean (type=type_error.bool)

You could define a simple decorator that can be used for every function you want to validate.您可以定义一个简单的装饰器,可用于您要验证的每个 function。 This decorator builds up on the inpsect module.这个装饰器建立在inpsect模块上。 The idea is to validate the function parameter (both positional and non-positional) against the default value of the corresponding argument in the function.这个想法是根据 function 中相应参数的默认值来验证 function 参数(位置和非位置)。

import inspect
from functools import wraps

def validate_func_args(func):
    def check_type(value, fn_param: inspect.Parameter):
        if not fn_param.default is fn_param.empty:
            _default = fn_param.default
            # We do not type check if the default value is None or if the argument is none
            is_none = _default is None or value is None

            if not is_none and type(value) != type(_default):
                raise TypeError(f"Invalid type encountered for {fn_param.name}")

    @wraps(func)
    def wrapper(*args, **kwargs):
        # First pass
        inspect.signature(foo).bind(*args, **kwargs)

        # Second pass (validate parameters with default values)
        args_count = len(args)
        fn_params = inspect.signature(foo).parameters

        for arg, param in zip(args, list(fn_params)[:args_count]):
            check_type(arg, fn_params[param])

        for kwarg, value in kwargs.items():
            check_type(value, fn_params[kwarg])

        # All good. Now call the function and return the result
        result = func(*args, **kwargs)
        return result

    return wrapper

Function definition Function定义

@validate_func_args
def foo(a, b=2, x=True):
    pass

Function call Function 来电

foo(10, None, "something") 

Output Output

TypeError: Invalid type encountered for x

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM