2010-06-30 77 views
3

我想写一个python函数装饰器,它测试某个函数的某些参数是否通过某些标准。对于例如,假设我想测试的一些参数总是偶数,那么我希望能够做这样的事(不是有效的Python代码)如何将特定参数传递给python中的装饰器

def ensure_even(n) : 
    def decorator(function) : 
    @functools.wraps(function) 
    def wrapper(*args, **kwargs): 
     assert(n % 2 == 0) 
     return function(*args, **kwargs) 
    return wrapper 
    return decorator 


@ensure_even(arg2) 
def foo(arg1, arg2, arg3) : pass 

@ensure_even(arg3) 
def bar(arg1, arg2, arg3) : pass 

但我想不出如何实现以上。有没有办法将特定参数传递给装饰器? (如上面的arg2 for foo和arg3 for bar)

谢谢!

+0

制作参数化装饰器的唯一方法是使用嵌套装饰器。 – 2010-06-30 19:07:35

回答

11

你可以这样做:

def ensure_even(argnum): 
    def fdec(func): 
    def f(*args, **kwargs): 
     assert(args[argnum] % 2 == 0) #or assert(not args[argnum] % 2) 
     return func(*args, **kwargs) 
    return f 
    return fdec 

那么接下来:

@ensure_even(1) #2nd argument must be even 
def test(arg1, arg2): 
    print(arg2) 

test(1,2) #succeeds 
test(1,3) #fails 
+0

是的,这就是我所做的,但我认为这只是我的C++背景,而不是一个更优雅的pythonic方式黑客:)谢谢无论如何。 – 2010-06-30 18:35:29

+1

@MK:如果您使用关键字参数而不是位置参数,那么您可以传递'ensure_even'作为关键字参数的名称。这可能会更好,因为将会有一个较少的硬编码数字,并且您的代码可能在添加新的参数后仍然存在,而无需更改参数。 – unutbu 2010-06-30 18:40:18

+0

@〜unutbu是的,这是一个好方法。 – jcao219 2010-06-30 18:45:38

4

我以前的答案错过了你的问题的要点。

这里有一个较长的解决方案:

def ensure_even(*argvars): 
    def fdec(func): 
    def f(*args,**kwargs): 
     for argvar in argvars: 
     try: 
      assert(not args[func.func_code.co_varnames.index(argvar)] % 2) 
     except IndexError: 
      assert(not kwargs[argvar] % 2) 
     return func(*args,**kwargs) 
    return f 
    return fdec 

,因此这是用法:

@ensure_even('a','b') 
def both_even(a,b): 
    print a,b 

@ensure_even('even') 
def first_even(even, arg2): 
    print even, arg2 

both_even(2,2) 
first_even(2,3) 
both_even(2,1) #fails 

虽然我不知道这是否会在所有情况下工作。

+0

啊反思。我不得不首先了解上述做法,但我明白了,这很酷。不过,我会坚持使用上述解决方案 – 2010-07-02 15:11:11