需求:
实现一个装饰器,它用来检查被装饰函数的参数类型,装饰器可以通过参数来指明函数参数类型,调用时如果检测出参数不匹配则抛出异常
@type_assert(str,int,int)
def f(a,b,c):
....
@type_assert(y=list)
def g(x,y):
....
思路:
1、提取函数签名,inspect.signature()
2、带参数的装饰器,也就是根据参数定制一个装饰器,可以看成是生产装饰器的工厂。每次调用type_assert,返回一个特定的装饰器,然后用它去修饰其他函数
代码:
import inspect
# 带参数的装饰器,制造工厂的工厂
def type_assert(*ty_args,**ty_kwargs):
def decorator(func):
# A...
func_sig = inspect.signature(func)
bind_type = func_sig.bind_partial(*ty_args,**ty_kwargs).arguments
def wrap(*args,**kwargs):
# B...
for name,obj in func_sig.bind(*args,**kwargs).arguments.items():
type_ = bind_type.get(name)
if type_:
if not isinstance(obj,type_):
raise TypeError('%s must be %s' % (name,type_))
return func(*args,**kwargs)
return wrap
return decorator
@type_assert(c=str)
def f(a,b,c):
pass
>>> def f(a,b,c):
... pass
...
... f_sig = inspect.signature(f)
>>> f_sig.parameters
mappingproxy(OrderedDict([('a', <Parameter "a">), ('b', <Parameter "b">), ('c', <Parameter "c">)]))
>>> pa = f_sig.parameters['a']
>>> pa.name
'a'
>>> pa.kind
<_ParameterKind.POSITIONAL_OR_KEYWORD: 1>
>>> pa.default
<class 'inspect._empty'>
>>> ba = f_sig.bind(int,int,str)
>>> ba.arguments
OrderedDict([('a', <class 'int'>), ('b', <class 'int'>), ('c', <class 'str'>)])
>>> ba.arguments['a']
<class 'int'>
>>> f_sig.bind(str,list)
Traceback (most recent call last):
File "<ipython-input-35-8b202b51ffc9>", line 1, in <module>
f_sig.bind(str,list)
File "/home/richardo/anaconda3/lib/python3.7/inspect.py", line 3002, in bind
return args[0]._bind(args[1:], kwargs)
File "/home/richardo/anaconda3/lib/python3.7/inspect.py", line 2917, in _bind
raise TypeError(msg) from None
TypeError: missing a required argument: 'c'
>>> f_sig.bind_partial(str,list)
<BoundArguments (a=<class 'str'>, b=<class 'list'>)>
>>>