How to use Python decorators to check function arguments?

后端 未结 8 1626
情歌与酒
情歌与酒 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条回答
  • 2020-11-28 23:28

    I have a slightly improved version of @jbouwmans sollution, using python decorator module, which makes the decorator fully transparent and keeps not only signature but also docstrings in place and might be the most elegant way of using decorators

    from decorator import decorator
    
    def check_args(**decls):
        """Decorator to check argument types.
    
        Usage:
    
        @check_args(name=str, text=str)
        def parse_rule(name, text): ...
        """
        @decorator
        def wrapper(func, *args, **kwargs):
            code = func.func_code
            fname = func.func_name
            names = code.co_varnames[:code.co_argcount]
            for argname, argtype in decls.iteritems():
                try:
                    argval = args[names.index(argname)]
                except IndexError:
                    argval = kwargs.get(argname)
                if argval is None:
                    raise TypeError("%s(...): arg '%s' is null"
                                % (fname, argname))
                if not isinstance(argval, argtype):
                    raise TypeError("%s(...): arg '%s': type is %s, must be %s"
                                % (fname, argname, type(argval), argtype))
        return func(*args, **kwargs)
    return wrapper
    
    0 讨论(0)
  • 2020-11-28 23:30

    From the Decorators for Functions and Methods:

    Python 2

    def accepts(*types):
        def check_accepts(f):
            assert len(types) == f.func_code.co_argcount
            def new_f(*args, **kwds):
                for (a, t) in zip(args, types):
                    assert isinstance(a, t), \
                           "arg %r does not match %s" % (a,t)
                return f(*args, **kwds)
            new_f.func_name = f.func_name
            return new_f
        return check_accepts
    

    Python 3

    In Python 3 func_code has changed to __code__ and func_name has changed to __name__.

    def accepts(*types):
        def check_accepts(f):
            assert len(types) == f.__code__.co_argcount
            def new_f(*args, **kwds):
                for (a, t) in zip(args, types):
                    assert isinstance(a, t), \
                           "arg %r does not match %s" % (a,t)
                return f(*args, **kwds)
            new_f.__name__ = f.__name__
            return new_f
        return check_accepts
    

    Usage:

    @accepts(int, (int,float))
    def func(arg1, arg2):
        return arg1 * arg2
    
    func(3, 2) # -> 6
    func('3', 2) # -> AssertionError: arg '3' does not match <type 'int'>
    

    arg2 can be either int or float

    0 讨论(0)
提交回复
热议问题