2013-06-04 18 views
21

我正在尋求將數據庫事務的邏輯封裝到with塊中;將代碼封裝在事務中並處理各種異常(鎖定問題)。這很簡單,但是我想讓塊在某些例外之後封裝代碼塊的重試。我看不出一種方式將其整齊地打包到上下文管理器中。將重試封裝爲`with`塊

是否可以在with語句中重複代碼?

我想使用它,就像這樣,這真的很乾淨。

def do_work(): 
    ... 
    # This is ideal! 
    with transaction(retries=3): 
     # Atomic DB statements 
     ... 
    ... 

我目前正在與一個裝飾處理這一點,但我更願意提供上下文管理器(或實際上兩者都有),所以我可以選擇包裹的幾行代碼在with塊代替內聯函數包裹在一個裝飾,這是我目前做的:

def do_work(): 
    ... 
    # This is not ideal! 
    @transaction(retries=3) 
    def _perform_in_transaction(): 
     # Atomic DB statements 
     ... 
    _perform_in_transaction() 
    ... 
+0

http://docs.python.org/release/2.5/whatsnew/pep-343.html看起來像它有如何實現上下文管理器的例子。 – Vlad

回答

4

由於裝飾只是函數本身,你可以做到以下幾點:

with transaction(_perform_in_transaction, retries=3) as _perf: 
    _perf() 

對於詳細信息,您需要實施transaction()作爲工廠方法,該工廠方法返回一個對象,其中__callable__()設置爲調用原始方法並重復執行,最多失敗次數爲retries; __enter__()__exit__()將被定義爲正常的數據庫事務上下文管理器。

您可以選擇設置transaction(),這樣它自己就可以執行最多retries的傳遞方法,這可能需要大約與實現上下文管理器相同的工作量,但意味着實際使用量將減少到僅僅transaction(_perform_in_transaction, retries=3)(這實際上相當於提供的裝飾器例子delnan)。

12

是否可以重複with語句中的代碼?

No.

正如前面所指出的在郵件列表線程,你可以通過裝飾調用傳遞的功能降低有點重複:這發生在我

def do_work(): 
    ... 
    # This is not ideal! 
    @transaction(retries=3) 
    def _perform_in_transaction(): 
     # Atomic DB statements 
     ... 
    # called implicitly 
    ... 
+0

啊,可惜它不支持。感謝您的線索鏈接。我喜歡讓呼叫隱含起來,使其更清潔。如果我想在'_perform_in_transaction'中設置/修改變量,我想我必須手動調用它,並返回我需要繼續執行'do_work'函數的其餘部分。 –

4

方式這樣做只是爲了實現標準數據庫事務context manager,但允許它在構造函數中使用retries參數。然後,我會將它包裝在您的方法實現中。像這樣的:

class transaction(object): 
    def __init__(self, retries=0): 
     self.retries = retries 
    def __enter__(self): 
     return self 
    def __exit__(self, exc_type, exc_val, traceback): 
     pass 

    # Implementation... 
    def execute(self, query): 
     err = None 
     for _ in range(self.retries): 
      try: 
       return self._cursor.execute(query) 
      except Exception as e: 
       err = e # probably ought to save all errors, but hey 
     raise err 

with transaction(retries=3) as cursor: 
    cursor.execute('BLAH') 
+1

您能詳細說明'self._cursor'中'_cursor'的來源嗎? –

+0

@MikeMüller我試圖利用一些常見的數據庫API習慣,而不會陷入實現細節中。'_cursor'應該是一個['Cursor'](http://www.python.org/dev/peps/pep-0249/#cursor-objects)對象,適用於所涉及的特定數據庫連接。完整的實現需要創建幷包含某種「連接」對象,以便實際執行數據庫事務。 –

+0

@HenryKeller我會做這樣的'def __init __(self,cursor,retries = 0):'並且在'__init__'這個'self._cursor = cursor'內。用法:用事務(遊標,重試= 3)作爲遊標:'。這有意義嗎? –