如何定义带参数装饰器?

需求:
实现一个装饰器,它用来检查被装饰函数的参数类型,装饰器可以通过参数来指明函数参数类型,调用时如果检测出参数不匹配则抛出异常

@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'>)>
>>> 

f(5,10,[])

posted @ 2020-11-11 15:05  Richardo-M-Lu  阅读(113)  评论(0编辑  收藏  举报