亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Python 中的with關鍵字使用詳解

 更新時間:2016年09月11日 08:59:53   投稿:hebedich  
這篇文章主要介紹了Python 中的with關鍵字使用詳解的相關資料,在Python中,with關鍵字是一個替你管理實現上下文協議對象的好東西,需要的朋友可以參考下

在 Python 2.5 中, with 關鍵字被加入。它將常用的 try ... except ... finally ... 模式很方便的被復用。看一個最經典的例子:

with open('file.txt') as f:
  content = f.read()

在這段代碼中,無論 with 中的代碼塊在執(zhí)行的過程中發(fā)生任何情況,文件最終都會被關閉。如果代碼塊在執(zhí)行的過程中發(fā)生了一個異常,那么在這個異常被拋出前,程序會先將被打開的文件關閉。

再看另外一個例子。

在發(fā)起一個數據庫事務請求的時候,經常會用類似這樣的代碼:

db.begin()

try:
  # do some actions
except:
  db.rollback()
  raise
finally:
  db.commit()

如果將發(fā)起事務請求的操作變成可以支持 with 關鍵字的,那么用像這樣的代碼就可以了:

with transaction(db):
  # do some actions

下面,詳細的說明一下 with 的執(zhí)行過程,并用兩種常用的方式實現上面的代碼。

with 的一般執(zhí)行過程

一段基本的 with 表達式,其結構是這樣的:

with EXPR as VAR:
  BLOCK

其中: EXPR 可以是任意表達式; as VAR 是可選的。其一般的執(zhí)行過程是這樣的:

  1. 計算 EXPR ,并獲取一個上下文管理器。
  2. 上下文管理器的 __exit()__ 方法被保存起來用于之后的調用。
  3. 調用上下文管理器的 __enter()__ 方法。
  4. 如果 with 表達式包含 as VAR ,那么 EXPR 的返回值被賦值給 VAR 。
  5. 執(zhí)行 BLOCK 中的表達式。
  6. 調用上下文管理器的 __exit()__ 方法。如果 BLOCK 的執(zhí)行過程中發(fā)生了一個異常導致程序退出,那么異常的 type 、 value 和 traceback (即 sys.exc_info()的返回值 )將作為參數傳遞給 __exit()__ 方法。否則,將傳遞三個 None 。

將這個過程用代碼表示,是這樣的:

mgr = (EXPR)
exit = type(mgr).__exit__ # 這里沒有執(zhí)行
value = type(mgr).__enter__(mgr)
exc = True

try:
  try:
    VAR = value # 如果有 as VAR
    BLOCK
  except:
    exc = False
    if not exit(mgr, *sys.exc_info()):
      raise
finally:
  if exc:
    exit(mgr, None, None, None)

這個過程有幾個細節(jié):

如果上下文管理器中沒有 __enter()__ 或者 __exit()__ 中的任意一個方法,那么解釋器會拋出一個 AttributeError 。
在 BLOCK 中發(fā)生異常后,如果 __exit()__ 方法返回一個可被看成是 True 的值,那么這個異常就不會被拋出,后面的代碼會繼續(xù)執(zhí)行。

接下來,用兩種方法來實現上面來實現上面的過程的吧。

實現上下文管理器類

第一種方法是實現一個類,其含有一個實例屬性 db 和上下文管理器所需要的方法 __enter()__ 和 __exit()__ 。

class transaction(object):
  def __init__(self, db):
    self.db = db

  def __enter__(self):
    self.db.begin()

  def __exit__(self, type, value, traceback):
    if type is None:
      db.commit()
    else:
      db.rollback()

了解 with 的執(zhí)行過程后,這個實現方式是很容易理解的。下面介紹的實現方式,其原理理解起來要復雜很多。

使用生成器裝飾器

在Python的標準庫中,有一個裝飾器可以通過生成器獲取上下文管理器。使用生成器裝飾器的實現過程如下:

from contextlib import contextmanager

@contextmanager
def transaction(db):
  db.begin()

  try:
    yield db
  except:
    db.rollback()
    raise
  else:
    db.commit()

第一眼上看去,這種實現方式更為簡單,但是其機制更為復雜。看一下其執(zhí)行過程吧:

  1. Python解釋器識別到 yield 關鍵字后, def 會創(chuàng)建一個生成器函數替代常規(guī)的函數(在類定義之外我喜歡用函數代替方法)。
  2. 裝飾器 contextmanager 被調用并返回一個幫助方法,這個幫助函數在被調用后會生成一個 GeneratorContextManager 實例。最終 with 表達式中的 EXPR 調用的是由 contentmanager 裝飾器返回的幫助函數。
  3. with 表達式調用 transaction(db) ,實際上是調用幫助函數。幫助函數調用生成器函數,生成器函數創(chuàng)建一個生成器。
  4. 幫助函數將這個生成器傳遞給 GeneratorContextManager ,并創(chuàng)建一個 GeneratorContextManager 的實例對象作為上下文管理器。
  5. with 表達式調用實例對象的上下文管理器的 __enter()__ 方法。
  6. __enter()__ 方法中會調用這個生成器的 next() 方法。這時候,生成器方法會執(zhí)行到 yield db 處停止,并將 db 作為 next() 的返回值。如果有 as VAR ,那么它將會被賦值給 VAR 。
  7. with 中的 BLOCK 被執(zhí)行。
  8. BLOCK 執(zhí)行結束后,調用上下文管理器的 __exit()__ 方法。 __exit()__ 方法會再次調用生成器的 next() 方法。如果發(fā)生 StopIteration 異常,則 pass 。
  9. 如果沒有發(fā)生異常生成器方法將會執(zhí)行 db.commit() ,否則會執(zhí)行 db.rollback() 。

再次看看上述過程的代碼大致實現:

def contextmanager(func):
  def helper(*args, **kwargs):
    return GeneratorContextManager(func(*args, **kwargs))
  return helper

class GeneratorContextManager(object):
  def __init__(self, gen):
    self.gen = gen

  def __enter__(self):
    try:
      return self.gen.next()
    except StopIteration:
      raise RuntimeError("generator didn't yield")

  def __exit__(self, type, value, traceback):
    if type is None:
      try:
        self.gen.next()
      except StopIteration:
        pass
      else:
        raise RuntimeError("generator didn't stop")
    else:
      try:
        self.gen.throw(type, value, traceback)
        raise RuntimeError("generator didn't stop after throw()")
      except StopIteration:
        return True
      except:
        if sys.exc_info()[1] is not value:
          raise

總結

Python的 with 表達式包含了很多Python特性?;c時間吃透 with 是一件非常值得的事情。

一些其他的例子

鎖機制

@contextmanager
def locked(lock):
  lock.acquired()
  try:
    yield
  finally:
    lock.release()

標準輸出重定向

@contextmanager
def stdout_redirect(new_stdout):
  old_stdout = sys.stdout
  sys.stdout = new_stdout
  try:
    yield
  finally:
    sys.stdout = old_stdout

with open("file.txt", "w") as f:
  with stdout_redirect(f):
    print "hello world"

參考資料

The Python “with” Statement by Example

PEP 343

相關文章

  • python批量生成條形碼的示例

    python批量生成條形碼的示例

    這篇文章主要介紹了python批量生成條形碼的示例,幫助大家更好的利用python處理圖形,感興趣的朋友可以了解下
    2020-10-10
  • Python Gluon參數和模塊命名操作教程

    Python Gluon參數和模塊命名操作教程

    這篇文章主要介紹了Python Gluon參數和模塊命名操作,結合實例形式詳細分析了Python Gluon模塊功能及基本使用技巧,需要的朋友可以參考下
    2019-12-12
  • Python Pillow Image Invert

    Python Pillow Image Invert

    今天小編就為大家分享一篇關于Python Pillow Image Invert,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-01-01
  • Python Pandas 如何shuffle(打亂)數據

    Python Pandas 如何shuffle(打亂)數據

    這篇文章主要介紹了Python Pandas 如何shuffle(打亂)數據,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-07-07
  • Python圖像運算之圖像灰度直方圖對比詳解

    Python圖像運算之圖像灰度直方圖對比詳解

    本篇文章將結合直方圖分別對比圖像灰度變換前后的變化,方便大家更清晰地理解灰度變換和閾值變換,文中的示例代碼講解詳細,需要的可以參考一下
    2022-08-08
  • 解決pyinstaller打包運行程序時出現缺少plotly庫問題

    解決pyinstaller打包運行程序時出現缺少plotly庫問題

    這篇文章主要介紹了解決pyinstaller打包運行程序時出現缺少plotly庫問題,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-06-06
  • Python建立Map寫Excel表實例解析

    Python建立Map寫Excel表實例解析

    這篇文章主要介紹了Python建立Map寫Excel表實例解析,具有一定借鑒價值,需要的朋友可以參考下
    2018-01-01
  • 1秒鐘使用python建立文件服務器的方法步驟

    1秒鐘使用python建立文件服務器的方法步驟

    本文主要介紹了1秒鐘使用python建立文件服務器的方法步驟,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-10-10
  • Python 矩陣轉置的幾種方法小結

    Python 矩陣轉置的幾種方法小結

    今天小編就為大家分享一篇Python 矩陣轉置的幾種方法小結,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-12-12
  • Flask中提供靜態(tài)文件的實例講解

    Flask中提供靜態(tài)文件的實例講解

    在本篇文章里小編給大家分享的是一篇關于Flask中提供靜態(tài)文件的實例及相關知識點詳解,有興趣的朋友們可以跟著學習下。
    2021-12-12

最新評論