要求具体到某个参数,那最好的办法自然是使用最新的语法annotations了,随手摘一段现在项目里的代码:
def _get_inner_function(f):
while hasattr(f, '__wrapped__'):
f = f.__wrapped__
return f
def _checktype(f):
_inner_f = _get_inner_function(f)
check_type_args = inspect.getfullargspec(_inner_f).annotations
if not check_type_args:
warnings.warn(UserWarning("Function " + repr(f) + " does not have annotations, checktype ignored."))
return f
@wraps(f)
async def _f(*args, **kwargs):
call_args = inspect.getcallargs(_inner_f, *args, **kwargs)
for k, v in list(call_args.items()):
if k in check_type_args:
call_args[k] = check_type(v, check_type_args[k])
return await f(**call_args)
return _f
注意_checktype这个注解在这里是用来修饰async函数的,可以自行去掉async和await。check_type是一个按规则检查类型并返回正确的值的函数,它在输入类型不符合规则的时候会抛出异常。这里使用的是 hubo1016/pychecktype 这里的实现,你也可以换成自己的实现,比如说仅仅检查是否为None。
使用方法类似于下面这样:
@_checktype
async def myfunc(self, param1: str, param2: (str, None), param3, param4: int = 0):
...
解释一下,_get_inner_function用来获取最内层的原始函数定义,这个利用了wraps修饰器在Python3最新版当中的特性,这就防止了其他装饰器对_checktype的功能造成干扰(不过也限定了其他装饰器不能修改传入参数)。annotations在Python中是函数的一部分,可以通过inspect中的函数等方法来获取,最后在装饰过的函数中比较传入的参数与注解,并进行必要的类型检查,就可以完成这个功能。链接中的check_type实现中,None值只能匹配None或者NoneType(None的类型),而不能匹配str、int等具体类型,也就实现了输入不能为None的要求。
如果要求返回值也有检查,原理也是一样的,返回值的注解可以在函数后用 -> ...的形式表述,它在annotations当中是以'return'为key的一个值。
当然这个装饰器只能在运行时检查,是没有静态检查的功能的。