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

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

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

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

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

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

再看另外一個(gè)例子。

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

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í)行過程,并用兩種常用的方式實(shí)現(xiàn)上面的代碼。

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

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

with EXPR as VAR:
  BLOCK

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

  1. 計(jì)算 EXPR ,并獲取一個(gè)上下文管理器。
  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ā)生了一個(gè)異常導(dǎo)致程序退出,那么異常的 type 、 value 和 traceback (即 sys.exc_info()的返回值 )將作為參數(shù)傳遞給 __exit()__ 方法。否則,將傳遞三個(gè) None 。

將這個(gè)過程用代碼表示,是這樣的:

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)

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

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

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

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

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

使用生成器裝飾器

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

from contextlib import contextmanager

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

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

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

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

再次看看上述過程的代碼大致實(shí)現(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特性?;c(diǎn)時(shí)間吃透 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é)合實(shí)例形式詳細(xì)分析了Python Gluon模塊功能及基本使用技巧,需要的朋友可以參考下
    2019-12-12
  • Python Pillow Image Invert

    Python Pillow Image Invert

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

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

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

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

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

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

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

    Python建立Map寫Excel表實(shí)例解析

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

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

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

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

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

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

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

最新評論