<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Python 中的 With 語句

          共 5040字,需瀏覽 11分鐘

           ·

          2020-08-04 11:13

          作者:Yujiaao

          來源:SegmentFault 思否




          總覽


          在Python中,您需要通過打開文件來訪問文件。您可以使用 open()函數(shù)來實(shí)現(xiàn)。Open 返回一個(gè)文件對(duì)象,該文件對(duì)象具有用于獲取有關(guān)已打開文件的信息和對(duì)其進(jìn)行操作的方法和屬性。





          with 語句


          使用 “with” 語句,使代碼簡(jiǎn)潔,處理異常也更優(yōu)雅。


          “with語句通過封裝常用的準(zhǔn)備工作和清除任務(wù)來簡(jiǎn)化異常處理?!?/span>


          此外,它將自動(dòng)關(guān)閉文件。with 語句提供了一種確保始終使用清理的方法。

          如果沒有 with 語句,我們將編寫如下內(nèi)容:

          ? ? ? ? ? ? ? ?

          file = open("welcome.txt")data = file.read()print(data)file.close()  # 文件用完一定要關(guān)閉





          with 語句用法


          'with' 語句是一個(gè)新的控制流結(jié)構(gòu),其基本結(jié)構(gòu)為:

          ? ? ? ? ? ? ? ?

          with expression [as variable]:    with-block


          使用 with 打開文件非常簡(jiǎn)單:使用open(filename) as file:

          ? ? ? ? ? ? ? ?

          with open("welcome.txt") as file: # file 做為對(duì)文件對(duì)象的引用
          data = file.read()
          # 使用 data 做點(diǎn)啥


          在寫入模式下打開output.txt

          ? ? ? ? ? ? ? ?

          with open('output.txt', 'w') as file:  # 輸出到file
          file.write('Hi there!')


          注意,我們不必編寫 file.close()。會(huì)被自動(dòng)調(diào)用。





          原理


          ' with '語句簡(jiǎn)化了以前使用try...finally塊來確保執(zhí)行清除代碼的代碼。在本節(jié)中,我將討論通常使用的語句。在下一節(jié)中,我將檢查實(shí)現(xiàn)細(xì)節(jié),并說明如何編寫用于此語句的對(duì)象。


          with 后面的表達(dá)式需支持上下文管理協(xié)議 (即,__enter__() 和__exit__() 方法)。

          ? ? ? ? ? ? ? ?

          with expression [as variable]:    with-block


          在執(zhí)行 with-block 之前調(diào)用對(duì)象的__enter __() 方法,因此可以運(yùn)行setup設(shè)置代碼??梢阅苓^ as 把表達(dá)式結(jié)果綁定到變量 variable(注意這里不是賦值到變量 variable)。


          with 塊的執(zhí)行完成后,即使該塊引發(fā)了異常,該對(duì)象的 __exit__() 方法也會(huì)被調(diào)用,因此可以運(yùn)行清理代碼。


          要在Python 2.5中啟用該語句,您需要在模塊中添加以下指令:


          from __future__ import with_statement


          該語句將始終在 Python 2.6 中啟用。

          現(xiàn)在,一些標(biāo)準(zhǔn)的 Python 對(duì)象支持上下文管理協(xié)議,并且可以與 'with' 語句一起使用。文件對(duì)象即是其中之一:

          ? ? ? ? ? ? ? ?

          with open('/etc/passwd', 'r') as f:    for line in f:        print line        ... 更多 ...


          執(zhí)行此語句后,即使for循環(huán)在代碼塊中途出現(xiàn)異常,f中的文件對(duì)象也將自動(dòng)關(guān)閉。


          注意:在這種情況下,f 是 open() 創(chuàng)建的同一對(duì)象 ,因?yàn)?file.__enter__()返回 self。


          threading 模塊的鎖和條件變量也支持 ?'with' 語句:

          ? ? ? ? ? ? ? ?

          lock = threading.Lock()with lock:    # 代碼臨界區(qū)    ...


          該鎖在執(zhí)行 with 塊之前獲取,并在該塊完成后始終釋放。


          decimal模塊中 的新 localcontext() 函數(shù)使保存和還原當(dāng)前decimal上下文變得容易,它封裝了計(jì)算所需的精度和舍入特征:

          ? ? ? ? ? ? ? ?

          from decimal import Decimal, Context, localcontext
          # 顯示默認(rèn)精度:28 位數(shù)字v = Decimal('578')print v.sqrt()
          with localcontext(Context(prec=16)): # 本代碼塊中使用16位精度. # 原始上下文將在退出塊后恢復(fù). print(v.sqrt())





          編寫上下文管理器


          在幕后,with 語句相當(dāng)復(fù)雜。大多數(shù)人只會(huì)在與現(xiàn)有對(duì)象一起使用 'with',并且不需要知道這些詳細(xì)信息,如果您想讓自己寫的類也支持 with語句,那就需要了解上下文管理器了。


          上下文管理協(xié)議的高級(jí)解釋是:


          • 該表達(dá)式將被求值并應(yīng)產(chǎn)生一個(gè)稱為``context manager''的對(duì)象。上下文管理器必須包含 __enter__() 和 __exit__() 方法。
          • 上下文管理器的 __enter__() 方法被調(diào)用。返回的值分配給 var 。如果不存在as var子句,則僅丟棄該值。
          • with 塊中的代碼被執(zhí)行。
          • 如果 with 塊引發(fā)異常, 則使用異常詳細(xì)信息調(diào)用__exit__(type,value,traceback),該異常詳細(xì)信息由sys.exc_info() 返回 。該方法的返回值控制是否重新引發(fā)異常:任何 False 值都會(huì)重新引發(fā)異常,True會(huì)抑制異常。通常很少需要抑制異常,因?yàn)槿绻@樣做,包含 'with' 語句的代碼的作者將永遠(yuǎn)不會(huì)意識(shí)到任何錯(cuò)誤。
          • 如果 with 塊沒有引發(fā)異常,則仍然會(huì)調(diào)用__exit__()方法,此時(shí)參數(shù)type,value和traceback都是 None。


          讓我們考慮一個(gè)例子。我不會(huì)提供詳細(xì)的代碼,而只會(huì)概述支持事務(wù)的數(shù)據(jù)庫所必需的方法。


          (對(duì)于不熟悉數(shù)據(jù)庫術(shù)語的人:將對(duì)數(shù)據(jù)庫的一組更改分組為一個(gè)事務(wù)??梢蕴峤皇聞?wù),這意味著將所有更改都寫入數(shù)據(jù)庫,也可以回滾,這意味著將所有更改都丟棄并刪除。數(shù)據(jù)庫未更改。有關(guān)更多信息,請(qǐng)參見任何數(shù)據(jù)庫教科書。)


          假設(shè)有一個(gè)代表數(shù)據(jù)庫連接的對(duì)象。我們的目標(biāo)是讓用戶編寫如下代碼:

          ? ? ? ? ? ? ? ?

          db_connection = DatabaseConnection()with db_connection as cursor:    cursor.execute('insert into ...')    cursor.execute('delete from ...')    # ... more operations ...


          如果塊中的代碼完美運(yùn)行,則應(yīng)該提交事務(wù);如果有異常,則應(yīng)回滾事務(wù)。這是我假設(shè)的DatabaseConnection的基本接口:

          ? ? ? ? ? ? ? ?

          class DatabaseConnection:    ...    def __enter__ (self):        # Code to start a new transaction        cursor = self.cursor()        return cursor


          該__enter __()方法是很簡(jiǎn)單的,只有到啟動(dòng)新的事務(wù)。對(duì)于此應(yīng)用程序,結(jié)果光標(biāo)對(duì)象將是有用的結(jié)果,因此該方法將返回它。然后,用戶可以添加as cursor到其 with 語句中,以將游標(biāo)綁定到變量名。

          ? ? ? ? ? ? ? ?

          class DatabaseConnection:    # Database interface    def cursor (self):        "Returns a cursor object and starts a new transaction"    def commit (self):        "Commits current transaction"    def rollback (self):        "Rolls back current transaction"


          該__exit __()方法有點(diǎn)復(fù)雜,該方法必須檢查是否發(fā)生異常。如果沒有異常,則提交事務(wù)。如果存在異常,則事務(wù)將回滾。


          在下面的代碼中,執(zhí)行會(huì)從函數(shù)的末尾開始,并返回默認(rèn)值None。None為假,因此將自動(dòng)重新引發(fā)異常。如果需要,可以更加明確,并 在標(biāo)記的位置添加return語句。

          ? ? ? ? ? ? ? ?

          class DatabaseConnection:    ...    def __exit__ (self, type, value, tb):        if tb is None:            # No exception, so commit            self.commit()        else:            # Exception occurred, so rollback.            self.rollback()            # return False





          contextlib 模塊


          contextlib 模塊提供了一些功能和裝飾器,這些功能和裝飾器對(duì)于編寫與 'with' 語句一起使用的對(duì)象很有用。


          裝飾器稱為 contextmanager,它使您可以編寫一個(gè)生成器函數(shù),而不用定義一個(gè)新類。生成器應(yīng)恰好產(chǎn)生一個(gè)值。直到y(tǒng)ield的代碼 將作為__enter __()方法執(zhí)行,并且yield的值將是該方法的返回值,該返回值將綁定到' with '語句的as子句中的變量(如果有)。屈服后的代碼將在 __exit __()方法中執(zhí)行。塊中引發(fā)的任何異常都將由yield語句引發(fā)。


          上一節(jié)中的數(shù)據(jù)庫示例可以使用以下裝飾器編寫為:

          ? ? ? ? ? ? ? ?

          from contextlib import contextmanager
          @contextmanagerdef db_transaction (connection): cursor = connection.cursor() try: yield cursor except: connection.rollback() raise else: connection.commit()
          db = DatabaseConnection()with db_transaction(db) as cursor:


          該contextlib模塊還具有嵌套(MGR1, MGR2,...)功能結(jié)合了一些上下文管理器,所以你不需要寫嵌套“不與 ”語句。在此示例中,單個(gè)' with '語句既啟動(dòng)數(shù)據(jù)庫事務(wù)并獲取線程鎖:

          ? ? ? ?? ? ? ? ?

          lock = threading.Lock()with nested (db_transaction(db), lock) as (cursor, locked):

          最后,Closeing(object)函數(shù)返回object,以便可以將其綁定到變量,并object.close()在塊的末尾調(diào)用。

          ? ? ? ? ? ? ? ?

          import urllib, sysfrom contextlib import closing
          with closing(urllib.urlopen('http://bixuebihui.com')) as f: for line in f: sys.stdout.write(line)


          參考:

          https://docs.python.org/2.5/whatsnew/pep-343.html





          點(diǎn)擊左下角閱讀原文,到?SegmentFault 思否社區(qū)?和文章作者展開更多互動(dòng)和交流。

          -?END -

          瀏覽 24
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲成人一级片 | 五月婷婷网 | 天天天日夜夜夜操 | 操骚逼小视频 | 黄色电影特黄一级片 |