How to use Python decorators to check function arguments?

后端 未结 8 1667
情歌与酒
情歌与酒 2020-11-28 22:56

I would like to define some generic decorators to check arguments before calling some functions.

Something like:

@checkArguments(types = [\'int\', \'         


        
8条回答
  •  旧时难觅i
    2020-11-28 23:23

    On Python 3.3, you can use function annotations and inspect:

    import inspect
    
    def validate(f):
        def wrapper(*args):
            fname = f.__name__
            fsig = inspect.signature(f)
            vars = ', '.join('{}={}'.format(*pair) for pair in zip(fsig.parameters, args))
            params={k:v for k,v in zip(fsig.parameters, args)}
            print('wrapped call to {}({})'.format(fname, params))
            for k, v in fsig.parameters.items():
                p=params[k]
                msg='call to {}({}): {} failed {})'.format(fname, vars, k, v.annotation.__name__)
                assert v.annotation(params[k]), msg
            ret = f(*args)
            print('  returning {} with annotation: "{}"'.format(ret, fsig.return_annotation))
            return ret
        return wrapper
    
    @validate
    def xXy(x: lambda _x: 10<_x<100, y: lambda _y: isinstance(_y,float)) -> ('x times y','in X and Y units'):
        return x*y
    
    xy = xXy(10,3)
    print(xy)
    

    If there is a validation error, prints:

    AssertionError: call to xXy(x=12, y=3): y failed )
    

    If there is not a validation error, prints:

    wrapped call to xXy({'y': 3.0, 'x': 12})
      returning 36.0 with annotation: "('x times y', 'in X and Y units')"
    

    You can use a function rather than a lambda to get a name in the assertion failure.

提交回复
热议问题