2013-08-20 26 views
0

我有一個函數,我的原因異常,我希望它是一個裝飾器。其代碼如下:如何捕捉裝飾器中的異常

def des(i): 
    def new_func(func): 
     if i == 1: 
      raise Exception 
     else: 
      return func 
    return new_func 


@des(1) 
def func(): 
    print "!!" 


if __name__ == '__main__': 
    try: 
     func() 
    except Exception: 
     print 'error' 

但輸出是:

Traceback (most recent call last): 
    File "D:/des.py", line 10, in <module> 
    @des(1) 
    File "D:/des.py", line 4, in new_func 
    raise Exception 
Exception 

這樣,我怎麼能捕獲此異常?

+0

我認爲這可能與您的問題有關:[捕獲裝飾器中的異常,同時允許調用者捕獲異常](http://stackoverflow.com/questions/4249939/how-do-i-catch-an-exception -in-a-decorator-but-allow-the-caller-catch-it-w-rq = 1) – user2412092

+0

它的工作原理,我明白它是如何工作的。謝謝:) – LiGa

回答

0

至於其他的答案已經解釋,當前的問題是,當裝飾被應用到該函數,而不是函數被調用你得到拋出的異常。

爲了解決這個問題,你需要讓裝飾器返回一個提升異常的函數。以下是如何可以工作:

import functools 

def des(i): 
    def decorator(func): 
     if i != 1: 
      return funC# no wrapper needed 

     @functools.wraps(func) 
     def raiser(*args, **kwargs): 
      raise Exception 

     return raiser 

    return decorator 

des功能是「裝飾工廠」。除了提供一個範圍來保存它返回的修飾器的參數i之外,它並沒有做任何其他的事情。

decorator函數檢查是否有任何特殊的需要完成。如果不是,它會返回修改後的功能。如果i==1,它返回一個自定義函數。

raiser函數是裝飾器的返回值,如果i==1。它在被調用時總會引發異常。應用於此的functools.wraps修飾器並不是嚴格必要的,但它使它看起來更像原始功能(相同的__name__,__doc__等)。

+0

這也是一個不錯的解決方案:'i == 1'和'!='之間的區別儘可能早地發生,但異常發生在通話時間,因爲有利。 – glglgl

2

當您的定義了函數時會引發異常。捕獲這個異常的唯一方法是:

try: 
    @des(1) 
    def func(): 
     print '!!' 
except: 
    print 'error' 

如果這是令人困惑,爲什麼這是造成異常,請記住你的代碼就相當於:

def func(): 
    print '!!' 
func = des(1)(func) 
# des(1) = new_func, so des(1)(func) is new_func(func) 
+0

但它引導func一個未定義的函數 – LiGa

+0

是的,如果在定義函數的過程中引發異常,則不會定義該函數。除了在'except'語句中爲'func'分配一個不同的函數,真的沒有辦法。如果我知道實際的代碼是什麼,那麼我可以提供更多的幫助,而不是這個例子。 –

+0

@LiGa是的,但你可以通過異常來檢測,並適當地處理。 – glglgl

1

所以,現在你的代碼基本上可以歸結爲此:

_des = des(1) 

def _func(); 
    print '!!' 

func = _des(func) 

您使用的des作爲裝飾的返回值,我認爲這是造成問題的原因。

我想你可能需要在該函數返回一個更多的時間窩:

def des(i): # container func. 
    def new_func(func): 
     def ret_func(*args, **kwargs): 
      if i == 1: 
       raise Exception 
      else: 
       return func(*args, **kwargs) 

     return ret_funC# return the func with the bound variable 
    return new_funC# return the func which creates the function w/ the bound var. 


@des(1) 
def func(): 
    print "!!" 
+0

我想你想讓你最內層的函數調用'func',而不僅僅是返回它。也就是說,用'return func()'替換'return func'。您可能想要使用可變參數語法('* args,** kwargs')來允許裝飾函數也採用一些參數。 – Blckknght

+0

@Blckknght但是,然後一級功能丟失。看看我的答案。 – glglgl

+0

@glglgl:我不確定我是否遵守。 cwallenpool的答案有適當數量的嵌套函數,儘管沒有像你的那樣整齊地呈現(並且沒有'()'使最內層的函數工作)。你實際上可以在'i == 1'上創建最內層的函數,如果不需要,就從裝飾器(第二層函數)返回'func'。 – Blckknght

0

我在這裏缺少一個功能水平。

ITYM

import functools 
def des(i): # this is the "decorator creator", called with des(1) 
    def deco(func): # this is returned and is the real decorator, called at function definition time 
     @functools.wraps(func) # sugar 
     def new_func(*a, **k): # and this is the function called on execution. 
      if i == 1: 
       raise Exception # I hope this is just for testing... better create a new exception for this 
      else: 
       return func(*a, **k) 
     return new_func 
    return deco 

@des(1) 
def func(): 
    print "!!" 

if __name__ == '__main__': 
    try: 
     func() 
    except Exception: 
     print 'error'