使用装饰器检查可选参数

时间:2014-08-21 14:23:21

标签: python decorator optional-parameters python-decorators

我试图找出如何编写装饰器以检查是否使用特定的可选参数调用该函数。这可能不是检查参数的pythonic方法,但我想知道使用装饰器的解决方案。以下是我正在寻找的一个例子:

@require_arguments("N", "p")  # Question here

def g(x,*args,**kwargs):
    if "N" not in kwargs: raise SyntaxError("missing N")
    if "p" not in kwargs: raise SyntaxError("missing p")
    print x

g(3,N=2) # Raise "missing p"

如何编写会引发相应错误的装饰器@require_arguments(*args)

2 个答案:

答案 0 :(得分:4)

你走了:

def require_arguments(*reqargs):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for arg in reqargs:
                if not arg in kwargs:
                    raise TypeError("Missing %s" % arg)
            return func(*args, **kwargs)
        return wrapper
    return decorator

@require_arguments("N", "p")  # Question here
def g(x,*args,**kwargs):
    print x

g(3,N=2) # Raise "missing p"

输出:

Traceback (most recent call last):
  File "arg.py", line 19, in <module>
    g(3,N=2) # Raise "missing p"
  File "arg.py", line 7, in wrapper
    raise ValueError("Missing %s" % arg)
ValueError: Missing p

答案 1 :(得分:1)

dano的回答是正确的,我可以添加的唯一建议是:

  • 使用functools.wraps创建装饰器,这样就可以保留函数的名称及其 docstring

  • 生成所有缺失参数的列表,而不是单个参数。我个人讨厌编译器说的时候:&#39; 你忘记了&#39;然后当我添加它时它仍然抱怨:&#39; 你忘记了b &#39;等等。

装饰者变成:

from functools import wraps

def require_arguments(*reqs):
    def decorator(func):
        @wraps(func)
        def decorated(*args, **kwargs):
            missing = [req for req in reqs if req not in kwargs]
            if missing:
                raise TypeError('missing ' + ', '.join(missing))
            return func(*args, **kwargs)
        return decorated
    return decorator

实施例

@require_arguments('N', 'p')
def g(x, *args, **kargs):
    """just return x"""
    print x

print g.__name__
print g.__doc__

try:
    print g(3)
except TypeError as e:
    print e

输出

g
just return x
missing N, p

更好的方法

如果您需要必要的参数,只需明确地询问它们:def g(x, N, p)。您仍然可以调用g(x=2, N=5, p=3)之类的函数来使代码更具表现力,即使您指定了参数,也可以使用不同的顺序。但是你将拥有一个固定的api(通常很好),你可以指定参数或使用固定的api顺序。

def f(a, b, c):
    print a, b, c

f(1, 2, 3)            # 1 2 3
f(c=1, b=2, a=3)      # 3 2 1