簡體   English   中英

python中的上下文管理器如何捕獲異常

[英]How are exceptions caught with context manager in python

我有以下方法來建立與數據庫的連接並最終將其拆除。 函數看起來像這樣

def read_db(self, sql_statement):

    conn = pymysql.connect(host=self.h,user=self.u,passwd=self.pw,
                           db=self.db,port=self.p)
    try:
        with conn.cursor() as cur:
            cur.execute(sql_statement)
            doStuffGeneratingException()

    except Exception:
        cur.rollback()
        raise

    finally:
        conn.close()   

現在,如果我不得不用上下文管理器替換它,我認為它看起來像

@contextmanager
def setup_sql():
    conn = pymysql.connect(host=self.h,user=self.u,passwd=self.pw,
                           db=self.db,port=self.p)
    yield conn.cursor()
    connection.cursor().close()
    self.connection.close()


def read_db(self, sql_statement):
    with setup_sql() as cur:
    try:
        cur.execute(sql_statement)
        doStuffGeneratingException()

    except:
         cur.rollback()
         raise

現在我的問題是

  1. 上下文管理器的上述解釋是否正確?
  2. 如果在contextmanager執行pymysql.connect(...)語句時發生錯誤,將如何處理? 它將如何冒泡到調用函數?
  3. 如果with實現中的doStuffGeneratingException()出現異常會發生什么? 控件會先到setup_sql執行yield之后的語句嗎?

1,有點。 整個 try/except 需要另一個級別的縮進。

def read_db(self, sql_statement):
    with setup_sql() as cur:
        try:
            cur.execute(sql_statement)
            doStuffGeneratingException()

        except:
             cur.rollback()
             raise

2、代碼中沒有處理任何地方的錯誤,因此python本身會報告異常並停止執行。 它可以在您選擇的任何地方捕獲。 在 setup_sql() 和 read_db() 內部都是可行的,但是如果您打算對此做些什么,通常您希望盡可能接近引發它們的異常處理。 要在 read_db() 中執行另一個 try: 塊,您需要使用 setup_sql():

def read_db(self, sql_statement):
    try:
        with setup_sql() as cur:
            try:
                cur.execute(sql_statement)
                doStuffGeneratingException()

            except:
                 # gets exceptions thrown by cur.execute() and doStuffGeneratingException() 
                 # will not process context manager statements after yield if flow doesn't continue in this function past this except block
                 cur.rollback()
                 raise
    except:
        # gets exceptions thrown by `with setup_sql() as cur:`
        # again none of the statements within the context manager after the statement that raises an exception will be executed
        pass

3,沒有。 一個例外是立即“返回”,它將擊中您的回滾,而重新引發它將中止您的 with 塊。 如果您希望上下文管理器完成,請捕獲異常並處理它們而無需重新引發。 如果您需要在那里引發異常並希望上下文管理器完成其工作,請設置一個標志變量並在上下文管理器關閉后引發或以另一種方式重構您的代碼以實現該目標。

我相信您實際上可以通過在方法內部的yield語句周圍放置 try / except / finally 來封裝上下文管理器中的錯誤處理,例如:

from contextlib import contextmanager

@contextmanager
def setup_sql():
    conn = pymysql.connect(host=self.h,user=self.u,passwd=self.pw,
                           db=self.db,port=self.p)
    cursor = conn.cursor()
    try:
        yield cursor
    except Exception:
        cursor.rollback()
        raise
    finally:
        cursor.close()
        conn.close()

def read_db(self, sql_statement):
    with setup_sql() as cur:
        cur.execute(sql_statement)
        doStuffGeneratingException()

我還沒有嘗試過這個,但是我在另一個 SO 問題上遇到了這個評論,該問題鏈接到關於@contextmanager的文檔,解釋了它是如何工作的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM