<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關鍵字原理詳解

          共 8308字,需瀏覽 17分鐘

           ·

          2021-04-24 02:21


          ??【當當書香節(jié)優(yōu)惠碼】??

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

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

          有效期:4.12-4.23

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

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

          詳細使用方法見文章: 

          推薦一本經典計算機基礎書籍(附當當優(yōu)惠碼)


          引言

          大家好,歡迎來到 Crossin的編程教室 !

          在編寫涉及到異常處理的資源管理時,需要使用 try/finally 代碼結構,這樣的結構一多會導致整體代碼結構 很臃腫繁瑣,不易讀、不美觀,因此早在 Python2.6 版本就推出了 with 關鍵字。

          with as 語句是 Pyhton 提供的一種簡化語法,適用于對資源進行訪問的場合,確保不管使用過程中是否發(fā)生異常都會執(zhí)行必要的清理操作,釋放資源。

          今天分享的這篇文章就給大家詳細講一講 with 關鍵字

          with操作文件

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

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

          同樣,對于數(shù)據(jù)庫,如果連接數(shù)過多而沒有及時關閉的話,就可能會出現(xiàn) Can not connect to MySQL server Too many connections,因為數(shù)據(jù)庫連接是一種非常昂貴的資源,不可能無限制的被創(chuàng)建。

          在代碼中經常會看見 with open(file) as f  對文件進行操作,其中 with 關鍵字到底有什么用處呢?讓我們一起了解一下其底層原理。來看看如何正確關閉一個文件。

          普通版:

          file = "test.txt"

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

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

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

          進階版:

          file = "test.txt"

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

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

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

          高級版:

          file = "test.txt"

          def fun3():
              """
              with 關鍵字
              """

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

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

          上下文管理器

          什么是上下文?

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

          例如 一個 APP 應用,在切換界面的時候,要保存你是在哪個屏幕跳過來的等等信息,以便你點擊返回的時候能正確跳回,如果不存肯定就無法正確跳回了。

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

          上下文管理器

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

          上下文管理器對象可以使用 with 關鍵字。

          file = "test.txt"

          def fun3():
              """
              with 關鍵字
              """

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

          基于類的上下文管理器

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

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


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

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

              def __enter__(self):
                  """
                  進入with as 語句的時候被with調用
                  返回值作為 as 后面的變量
                  """

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

              def __exit__(self, exc_type, exc_value, exc_traceback):
                  """
                  離開with語句的時候被with調用
                  """

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


          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 語句結束")


          if __name__ == '__main__':
              main()

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

          因為 File 類實現(xiàn)了上下文管理器,現(xiàn)在就可以使用 with 語句了。其運行結果如下:

          __enter__ called
          with 代碼塊
          __exit__ called
          exc_type:  None
          exc_value:  None
          exc_traceback:  None
          文件關閉操作
          with 語句結束

          除0異常 的代碼注釋去了看看結果

          __enter__ called
          with 代碼塊
          __exit__ called
          exc_type:  <class 'ZeroDivisionError'>
          exc_value:
            division by zero
          exc_traceback:  <traceback object at 0x0000021F0780BCC8>
          文件關閉操作
          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
          這樣,你就無需顯示地調用 close 方法了,由系統(tǒng)自動去調用,哪怕中間遇到異常 close 方法也會被調用。

          基于contextmanager裝飾器

          Python 在 contextlib 模塊中還提供了一個 contextmanager 的裝飾器,更進一步簡化了上下文管理器的實現(xiàn)方式。通過 yield 將函數(shù)分割成兩部分,yield 之前的語句在  __enter__  方法中執(zhí)行,yield 之后的語句在 __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("文件關閉操作")

          調用

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

          print("with 語句結束")

          結果

          file_manager() called
          with 代碼塊
          文件關閉操作
          with 語句結束

          總結

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

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

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

          作者:編程小灰

          來源:新建文件夾X


          _往期文章推薦_

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




          如需了解付費精品課程教學答疑服務
          請在Crossin的編程教室內回復: 666

          瀏覽 44
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                  亚洲日韩欧美视频 | 狠狠撸伊人在线 | 日本黄A级A片国产免费 | 色婷婷国产成人精品视频 | 国产视频高清无码 |