1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Python中的with關(guān)鍵字原理詳解

        共 8308字,需瀏覽 17分鐘

         ·

        2021-04-24 02:21


        ??【當(dāng)當(dāng)書(shū)香節(jié)優(yōu)惠碼】??

        200-30 優(yōu)惠碼:NR9EZR 

        300-60 優(yōu)惠碼:HNZVTA 

        有效期:4.12-4.23

        可疊加官方滿減優(yōu)惠;限當(dāng)當(dāng)自營(yíng)(教輔教材除外)

        當(dāng)當(dāng)小程序、APP 和網(wǎng)站都可使用

        詳細(xì)使用方法見(jiàn)文章: 

        推薦一本經(jīng)典計(jì)算機(jī)基礎(chǔ)書(shū)籍(附當(dāng)當(dāng)優(yōu)惠碼)


        引言

        大家好,歡迎來(lái)到 Crossin的編程教室 !

        在編寫涉及到異常處理的資源管理時(shí),需要使用 try/finally 代碼結(jié)構(gòu),這樣的結(jié)構(gòu)一多會(huì)導(dǎo)致整體代碼結(jié)構(gòu) 很臃腫繁瑣,不易讀、不美觀,因此早在 Python2.6 版本就推出了 with 關(guān)鍵字。

        with as 語(yǔ)句是 Pyhton 提供的一種簡(jiǎn)化語(yǔ)法,適用于對(duì)資源進(jìn)行訪問(wèn)的場(chǎng)合,確保不管使用過(guò)程中是否發(fā)生異常都會(huì)執(zhí)行必要的清理操作,釋放資源。

        今天分享的這篇文章就給大家詳細(xì)講一講 with 關(guān)鍵字。

        with操作文件

        對(duì)于系統(tǒng)資源如文件、數(shù)據(jù)庫(kù)連接、socket 而言,應(yīng)用程序打開(kāi)這些資源并執(zhí)行完業(yè)務(wù)邏輯之后,必須做的一件事就是要關(guān)閉(釋放)該資源。

        比如 Python 程序打開(kāi)一個(gè)文件,往文件中寫內(nèi)容,寫完之后,就要關(guān)閉該文件,如果不關(guān)閉會(huì)出現(xiàn)什么情況呢?極端情況下會(huì)出現(xiàn) Too many open files 的錯(cuò)誤,因?yàn)橄到y(tǒng)允許你打開(kāi)的最大文件數(shù)量是有限的。

        同樣,對(duì)于數(shù)據(jù)庫(kù),如果連接數(shù)過(guò)多而沒(méi)有及時(shí)關(guān)閉的話,就可能會(huì)出現(xiàn) Can not connect to MySQL server Too many connections,因?yàn)閿?shù)據(jù)庫(kù)連接是一種非常昂貴的資源,不可能無(wú)限制的被創(chuàng)建。

        在代碼中經(jīng)常會(huì)看見(jiàn) with open(file) as f  對(duì)文件進(jìn)行操作,其中 with 關(guān)鍵字到底有什么用處呢?讓我們一起了解一下其底層原理。來(lái)看看如何正確關(guān)閉一個(gè)文件。

        普通版:

        file = "test.txt"

        def fun1():
            """
            普通版
            """

            f = open(file, "w")
            f.write("hello python")
            f.close()

        這樣寫有一個(gè)潛在的問(wèn)題,如果在調(diào)用 write 的過(guò)程中,出現(xiàn)了異常進(jìn)而導(dǎo)致后續(xù)代碼無(wú)法繼續(xù)執(zhí)行,close 方法無(wú)法被正常調(diào)用,因此資源就會(huì)一直被該程序占用者釋放。那么該如何改進(jìn)代碼呢?

        進(jìn)階版:

        file = "test.txt"

        def fun2():
            """
            異常處理
            """

            try:
                f = open(file, "w")
                f.write("hello python")
            except Exception as e:
                print(e)
            finally:
                f.close()

        改良版本的程序是對(duì)可能發(fā)生異常的代碼處進(jìn)行 try 捕獲,使用 try/finally 語(yǔ)句,該語(yǔ)句表示如果在 try代碼塊中程序出現(xiàn)了異常,后續(xù)代碼就不再執(zhí)行,而直接跳轉(zhuǎn)到 except 代碼塊。而無(wú)論如何,finally 塊的代碼最終都會(huì)被執(zhí)行。因此,只要把 close 放在 finally 代碼中,文件就一定會(huì)關(guān)閉。

        高級(jí)版:

        file = "test.txt"

        def fun3():
            """
            with 關(guān)鍵字
            """

            with open(file, "w"as f:
                f.write("hello python")

        一種更加簡(jiǎn)潔、優(yōu)雅的方式就是用 with 關(guān)鍵字。open 方法的返回值賦值給變量 f,當(dāng)離開(kāi) with 代碼塊的時(shí)候,系統(tǒng)會(huì)自動(dòng)調(diào)用 f.close() 方法, with 的作用和使用 try/finally 語(yǔ)句是一樣的。那么它的實(shí)現(xiàn)原理是什么?在講 with 的原理前要涉及到另外一個(gè)概念,就是 上下文管理器(Context Manager)

        上下文管理器

        什么是上下文?

        上下文在不同的地方表示不同的含義。在編程中 context 上下文其實(shí)說(shuō)白了就是環(huán)境。

        例如 一個(gè) APP 應(yīng)用,在切換界面的時(shí)候,要保存你是在哪個(gè)屏幕跳過(guò)來(lái)的等等信息,以便你點(diǎn)擊返回的時(shí)候能正確跳回,如果不存肯定就無(wú)法正確跳回了。

        再比如線程、協(xié)程進(jìn)行任務(wù)切換時(shí),程序怎么能知道切換到另一個(gè)任務(wù),是從頭開(kāi)始執(zhí)行還是從中間呢?其上下文就起到作用,就是任務(wù)本身會(huì)對(duì)其環(huán)境進(jìn)行保存,做到哪里了,做了多少,各種狀態(tài)都會(huì)標(biāo)識(shí)記錄,從而形成了上下文環(huán)境,因此在切換時(shí)根據(jù)每個(gè)任務(wù)的上下文環(huán)境,繼續(xù)執(zhí)行,從而達(dá)到多任務(wù)。

        上下文管理器

        任何類實(shí)現(xiàn)了 __enter__()__exit__() 方法的對(duì)象都可稱之為上下文管理器。

        上下文管理器對(duì)象可以使用 with 關(guān)鍵字。

        file = "test.txt"

        def fun3():
            """
            with 關(guān)鍵字
            """

            with open(file, "w"as f:
                # with 代碼塊
                f.write("hello python")
                
            print("with 語(yǔ)句結(jié)束")
        • __enter__(self):進(jìn)入上下文管理器自動(dòng)調(diào)用的方法,該方法會(huì)在 with as 代碼塊 執(zhí)行之前執(zhí)行。如果 with 語(yǔ)句有 as子句,那么該方法的返回值會(huì)被賦值給 as 子句后的變量;該方法可以返回多個(gè)值,因此在 as 子句后面也可以指定多個(gè)變量(多個(gè)變量必須由()括起來(lái)組成元組)。
        • __exit__(self, exc_type, exc_value, exc_traceback):退出上下文管理器自動(dòng)調(diào)用的方法。該方法會(huì)在 with as 代碼塊 執(zhí)行之后執(zhí)行。如果 with as 代碼塊成功執(zhí)行結(jié)束,程序自動(dòng)調(diào)用該方法,調(diào)用該方法的三個(gè)參數(shù)都為 None,如果 with as 代碼塊 因?yàn)?異常而中止,程序也自動(dòng)調(diào)用該方法,使用 sys.exc_info 得到的異常信息將作為調(diào)用該方法的參數(shù)。

        基于類的上下文管理器

        我們可以模擬實(shí)現(xiàn)一個(gè)自己的文件類,讓該類實(shí)現(xiàn) __enter__()__exit__()方法。

        """
        with關(guān)鍵字的實(shí)現(xiàn)原理
        上下文管理器
        """


        # 基于類實(shí)現(xiàn)上下文管理器
        class File(object):

            def __init__(self, filename, mode):
                self.filename = filename
                self.mode = mode
                self.file = None

            def __enter__(self):
                """
                進(jìn)入with as 語(yǔ)句的時(shí)候被with調(diào)用
                返回值作為 as 后面的變量
                """

                print("__enter__ called")
                self.file = open(self.filename, self.mode)
                return self.file

            def __exit__(self, exc_type, exc_value, exc_traceback):
                """
                離開(kāi)with語(yǔ)句的時(shí)候被with調(diào)用
                """

                print("__exit__ called")
                print("exc_type: ", exc_type)
                print("exc_value: ", exc_value)
                print("exc_traceback: ", exc_traceback)
                self.file.close()
                print("文件關(guān)閉操作")


        def main():

            with File("test.txt""w"as f:
                print("with 代碼塊")
                f.write("hello python1")
                f.write("hello python2")
                # a = 1 / 0
                f.write("hello python3")

            print("with 語(yǔ)句結(jié)束")


        if __name__ == '__main__':
            main()

        __enter__() 方法返回資源對(duì)象,這里就是你將要打開(kāi)的那個(gè)文件對(duì)象,__exit__()方法處理一些清除工作。

        因?yàn)?File 類實(shí)現(xiàn)了上下文管理器,現(xiàn)在就可以使用 with 語(yǔ)句了。其運(yùn)行結(jié)果如下:

        __enter__ called
        with 代碼塊
        __exit__ called
        exc_type:  None
        exc_value:  None
        exc_traceback:  None
        文件關(guān)閉操作
        with 語(yǔ)句結(jié)束

        除0異常 的代碼注釋去了看看結(jié)果

        __enter__ called
        with 代碼塊
        __exit__ called
        exc_type:  <class 'ZeroDivisionError'>
        exc_value:
          division by zero
        exc_traceback:  <traceback object at 0x0000021F0780BCC8>
        文件關(guān)閉操作
        Traceback (most recent call last):
          File "C:\Users\Administrator\Desktop\pycode\withdemo.py", line 113in <module>
            main()
          File "C:\Users\Administrator\Desktop\pycode\withdemo.py", line 106in main
            a = 1 / 0
        ZeroDivisionError: division by zero
        這樣,你就無(wú)需顯示地調(diào)用 close 方法了,由系統(tǒng)自動(dòng)去調(diào)用,哪怕中間遇到異常 close 方法也會(huì)被調(diào)用。

        基于contextmanager裝飾器

        Python 在 contextlib 模塊中還提供了一個(gè) contextmanager 的裝飾器,更進(jìn)一步簡(jiǎn)化了上下文管理器的實(shí)現(xiàn)方式。通過(guò) yield 將函數(shù)分割成兩部分,yield 之前的語(yǔ)句在  __enter__  方法中執(zhí)行,yield 之后的語(yǔ)句在 __exit__ 方法中執(zhí)行。緊跟在 yield 后面的值是函數(shù)的返回值。

        # 基于contextmanager裝飾器
        from contextlib import contextmanager

        @contextmanager
        def file_manager(name, mode):
            print("file_manager() called")
            try:
                f = open(name, mode)
                yield f
            finally:
                f.close()
                print("文件關(guān)閉操作")

        調(diào)用

        with file_manager('test.txt''w'as f:
            print("with 代碼塊")
            f.write('hello world')
            # a = 1 / 0

        print("with 語(yǔ)句結(jié)束")

        結(jié)果

        file_manager() called
        with 代碼塊
        文件關(guān)閉操作
        with 語(yǔ)句結(jié)束

        總結(jié)

        Python 提供了 with 語(yǔ)法用于簡(jiǎn)化資源操作的后續(xù)清除操作,是 try/finally 的替代方法,實(shí)現(xiàn)原理建立在上下文管理器之上。此外,Python 還提供了一個(gè) contextmanager 裝飾器,更進(jìn)一步簡(jiǎn)化上下文管理器的實(shí)現(xiàn)方式?;陬惡突?contextmanager 的上下文管理器,這兩者在功能上是一致的。只不過(guò),基于類的上下文管理器 更加靈活,適用于大型的系統(tǒng)開(kāi)發(fā),而基于 contextmanager 的上下文管理器 更加方便、簡(jiǎn)潔,適用于中小型程序

        無(wú)論使用哪一種,不要忘記在方法 __exit__() 或者是 finally 塊中釋放資源,這一點(diǎn)尤其重要。

        如果文章對(duì)你有幫助,歡迎轉(zhuǎn)發(fā)/點(diǎn)贊/收藏~

        作者:編程小灰

        來(lái)源:新建文件夾X


        _往期文章推薦_

        提高開(kāi)發(fā)效率,從避免濫用try開(kāi)始




        如需了解付費(fèi)精品課程教學(xué)答疑服務(wù)
        請(qǐng)?jiān)?strong style="max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;">Crossin的編程教室內(nèi)回復(fù): 666

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

        手機(jī)掃一掃分享

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

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            温迪被吸乳羞羞动漫 | 小舞3d同人18av黄漫网站 | 高hnp女高校花丰满h短篇合集 | 99er热精品视频 | 天堂网2016 | 国产男女AV | by73777在线看片 | 久久13p | 欧美色图一区二区三区 | 自拍偷拍国内 |