问题:通过装饰器想对函数参数进行强制类型检查,并且指定所有参数类型,也可以指定部分参数类型
未装饰的函数:
def spam(x, y, z=42): print(x,y,z) spam('hello',1) #hello 1 42
写装饰器前,运用到了inspect中的signature签名函数,signature()获取函数的参数签名信息:
sig = signature(spam) print(sig) #(x, y, z=42) print(type(sig.parameters['x'].name)) #<class 'str'> print(type(sig.parameters)) #<class 'mappingproxy'> print(sig.parameters.items()) #odict_items([('x', <Parameter "x">), ('y', <Parameter "y">), ('z', <Parameter "z=42">)])
bind_partial() 方法来执行从指定类型到名称的部分绑定:
bound_types = sig.bind_partial(int,str,z=int) #允许忽略任何参数(int,z=int) print(bound_types) #<BoundArguments (x=<class 'int'>, z=<class 'int'>)> print(bound_types.arguments) #OrderedDict([('x', <class 'int'>), ('z', <class 'int'>)]) print(bound_types.arguments['x']) #<class 'int'>
bind() 跟bind_partial() 类似,但是它不允许忽略任何参数:
bound_values = sig.bind(1,2,3) #不允许忽略任何参数 print(bound_values) #<BoundArguments (x=1, y=2, z=3)> print(bound_values.arguments) #OrderedDict([('x', 1), ('y', 2), ('z', 3)])
了解完这些之后,开始实现该装饰器了~
下面是使用装饰器技术来实现 @typeassert
from inspect import signature from functools import wraps def typeassert(*ty_args,**ty_kwargs): def decorate(func): if not __debug__: return func sig = signature(func) bound_types = sig.bind_partial(*ty_args,**ty_kwargs).arguments @wraps(func) def wrapper(*args,**kwargs): bound_values = sig.bind(*args,**kwargs) for name,value in bound_values.arguments.items(): if name in bound_types: if not isinstance(value,bound_types[name]): raise TypeError('Argument {} must be {}'.format(name,bound_types[name])) return func(*args,**kwargs) return wrapper return decorate @typeassert(str,z=int) def spam(x, y, z=42): print(x,y,z) spam(1,'ds','www') #TypeError: Argument x must be <class 'str'>