2010-06-30 111 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

相關問題