欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Python 中的with關(guān)鍵字使用詳解

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

在 Python 2.5 中, with 關(guān)鍵字被加入。它將常用的 try ... except ... finally ... 模式很方便的被復(fù)用??匆粋€最經(jīng)典的例子:

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

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

再看另外一個例子。

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

db.begin()

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

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

with transaction(db):
  # do some actions

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

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

一段基本的 with 表達(dá)式,其結(jié)構(gòu)是這樣的:

with EXPR as VAR:
  BLOCK

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

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

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

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

接下來,用兩種方法來實現(xiàn)上面來實現(xiàn)上面的過程的吧。

實現(xiàn)上下文管理器類

第一種方法是實現(xiàn)一個類,其含有一個實例屬性 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í)行過程后,這個實現(xiàn)方式是很容易理解的。下面介紹的實現(xiàn)方式,其原理理解起來要復(fù)雜很多。

使用生成器裝飾器

在Python的標(biāo)準(zhǔn)庫中,有一個裝飾器可以通過生成器獲取上下文管理器。使用生成器裝飾器的實現(xiàn)過程如下:

from contextlib import contextmanager

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

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

第一眼上看去,這種實現(xiàn)方式更為簡單,但是其機(jī)制更為復(fù)雜??匆幌缕鋱?zhí)行過程吧:

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

再次看看上述過程的代碼大致實現(xiàn):

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

總結(jié)

Python的 with 表達(dá)式包含了很多Python特性。花點時間吃透 with 是一件非常值得的事情。

一些其他的例子

鎖機(jī)制

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

標(biāo)準(zhǔn)輸出重定向

@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

相關(guān)文章

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

    python批量生成條形碼的示例

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

    Python Gluon參數(shù)和模塊命名操作教程

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

    Python Pillow Image Invert

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

    Python Pandas 如何shuffle(打亂)數(shù)據(jù)

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

    Python圖像運(yùn)算之圖像灰度直方圖對比詳解

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

    解決pyinstaller打包運(yùn)行程序時出現(xiàn)缺少plotly庫問題

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

    Python建立Map寫Excel表實例解析

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

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

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

    Python 矩陣轉(zhuǎn)置的幾種方法小結(jié)

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

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

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

最新評論