• python cookbook第三版学习笔记二十一:利用装饰器强制函数上的类型检查


    在演示实际代码前,先说明我们的目标:能对函数参数类型进行断言,类似下面这样:

    @typeassert(int, int)

    ... def add(x, y):

    ...     return x + y

    add(2, 'hello')
    Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
        File "contract.py", line 33, in wrapper
    TypeError: Argument y must be <class 'int'>
     

    我们可以自己实现这样一个装饰器。首先介绍下inspect.signature

    sig=signature(test_func)

    print(sig)

    print(sig.parameters)运行结果:

    (x, y, z=42)

    OrderedDict([('x', <Parameter "x">), ('y', <Parameter "y">), ('z', <Parameter "z=42">)])signature方法将函数的参数签名信息转换成一个可调用对象

     

    我们还可以使用sig.bind_partial()方法来执行从指定类型到名称的部分绑定。比如下面的绑定,这里将z这个参数绑定成了字节形式。返回结果是一个有序字典,这个字典会将参数名以函数签名中相同顺序映射到指定的类型值上面去

    bound_types=sig.bind_partial(int,z=bytes)

    print(bound_types.arguments)

    OrderedDict([('x', <class 'int'>), ('z', <class 'bytes'>)])

     

    另外一个是sig.bind方法。bind不允许忽略任何参数。Bind会将参数的值和变量名称绑定在一起,通过遍历就可以得出参数的名称和值。

    bound_values=sig.bind(1,2,3)

    print(bound_values)

    for name,value in bound_values.arguments.items():

    print(name,value)

    运行结果:

    <BoundArguments (x=1, y=2, z=3)>

    x 1

    y 2

    z 3

     

    通过bind_partial和bind方法我们就可以做到对参数的检查。bind_partial来对参数类型做强制绑定,然后用bind方法遍历出所有的参数进行参数核查。方法如下。

                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])

                                )

     

    装饰器的代码如下:

    def typeassert(*ty_args, **ty_kwargs):

        def decorate(func):

            # If in optimized mode, disable type checking

            if not __debug__:

                return func

            # Map function argument names to supplied types

            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)

                # Enforce type assertions across supplied arguments

                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(int,z=int)

    def para_check(x,y,z=3):

    print(x,y,z)

     

    if __name__=="__main__":

    para_check(1,2,'str')

    运行结果:由于z这个参数赋的是一个字符,因此抛出异常。

    Traceback (most recent call last):

      File "D:/py_prj/test2/cookbook.py", line 266, in <module>

        para_check(1,2,'str')

      File "D:/py_prj/test2/cookbook.py", line 254, in wrapper

        'Argument {} must be {}'.format(name, bound_types[name])

    TypeError: Argument z must be <class 'int'>

  • 相关阅读:
    XMPP即时通讯资料记录
    iOS 图片裁剪与修改
    iOS开发xcode报错:"xxxxxx"has been modified since the precompiled header was built
    模糊数学课件(清晰易懂)
    几个可用于数据挖掘和统计分析的java库
    java中list集合的内容,如何使用像数据库中group by形式那样排序
    spark java 代码example
    spark 编程向导
    一个深度学习博客
    Selenium2(WebDriver)_如何判断WebElement元素对象是否存在
  • 原文地址:https://www.cnblogs.com/zhanghongfeng/p/10190781.html
Copyright © 2020-2023  润新知