46. 定义带参数的装饰器

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

@type_assert(str, int, int)
def f(a, b, c):
    ...
    
@type_assert(y=list)
def g(x, y):
    ...

解决方案:

提取函数签名:inspect.signature()函数。

带参数的装饰器,也就是根据参数定制化一个装饰器,可以看成生产装饰器的工厂(装饰器可以看成是生产被装饰函数的工厂)。每次调用带参数的装饰器,返回一个特定的装饰器,然后使用该特定的装饰器去装饰其他函数。


  • 对于inspect.signature()函数:
inspect.signature(callable, *, follow_wrapped=True)

返回给定可调用对象的Signature对象。inspect.Signature类方法:

Signature.bind(*args, **kwargs)

创建从位置和关键字参数到参数的映射,绑定所有参数。如果*arg**kwarg匹配签名则返回BoundArguments,否则引发TypeError。

bind_partial(*args, **kwargs)

Signature.bind()相同,但是允许省略一些必需的参数(类似functools.partial()),绑定部分参数。返回BoundArguments,如果传递的参数与签名不匹配,则引发TypeError。

>>> import inspect

>>> 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>
>>> f_sig.bind(int, int, str)
<BoundArguments (a=<class 'int'>, b=<class 'int'>, c=<class 'str'>)>

>>> ba = f_sig.bind(int, int, str)

>>> ba.arguments
OrderedDict([('a', <class 'int'>), ('b', <class 'int'>), ('c', <class 'str'>)])

>>> ba.arguments['a']
<class 'int'>

  • 方案示例:
import inspect

def type_assert(*ty_args, **ty_kwargs):
    def decorator(func):
        func_sig = inspect.signature(func)              #获取函数签名对象
        bind_type = func_sig.bind_partial(*ty_args, **ty_kwargs).arguments              #以参数为key建立OrderedDict,value为绑定的type
        def wrap(*args, **kwargs):
            for name, obj in func_sig.bind(*args, **kwargs).arguments.items():              #obj是参数name的对象
                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(int, list, str)                #指定类型分别为int,list,str
def f(a, b, c):
    pass

f(5, [], 'abc')
f(5, 10, 'abc')             #类型不匹配会报错

结果:

Traceback (most recent call last):
  File "h:\Python Practice\twenty-four\c1.py", line 22, in <module>
    f(5, 10, 'abc')
  File "h:\Python Practice\twenty-four\c1.py", line 12, in wrap
    raise TypeError('%s must be %s' % (name, type_))
TypeError: b must be <class 'list'>

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值