问题:通过装饰器想对函数参数进行强制类型检查,并且指定所有参数类型,也可以指定部分参数类型
未装饰的函数:
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'>