一文弄懂Python上下文管理器和with用法

2021-02-19 python爬蟲人工智慧大數據


導讀:pythoners都知道有個關鍵字叫"with",它可以實現使用某些"臨時"聲明的對象,而之後"什麼也不用管",這個用法在python中叫上下文管理器。本文帶你快速入門上下文管理器和with用法。

上下文管理器,英文context managers,在python官方文檔中這樣描述:

上下文管理器是一個對象,它定義了在執行 with 語句時要建立的運行時上下文。 上下文管理器處理進入和退出所需運行時上下文以執行代碼塊。 通常使用 with 語句(在 with 語句中描述),但是也可以通過直接調用它們的方法來使用。

那麼可能還會有進一步的疑問:"上下文"又是什麼呢?

知乎某大佬關於"上下文"的解釋

如果還是難懂,那就舉個例子。在python中,寫入文件的基本操作可能是這樣的:

1f = open('a.txt', 'w')
2f.write('22')
3f.close()

如果考慮在文件操作期間可能會觸發異常、造成文件不能關閉,那麼比較安全的改進寫法是:

1f = open('a.txt', 'w')
2try:
3    f.write('22')
4except:
5    print('File eroor!')
6finally:
7    f.close()

當使用with包裝上下文管理器後,那麼只需要這樣:

1with open('b.txt', 'w') as f:
2    f.write('22')

可能會有這樣疑問,不就是節省了一行文件關閉的代碼而已嘛,也沒看出有多高效啊!

這裡先拋出結論:使用with管理上下文不僅可以在執行with語句體後自動執行退出操作(即__exit__方法定義語句),更重要的是能夠在發生異常時,確保始終能執行退出操作、釋放相應的資源。

在PEP343(該PEP文檔由python之父龜叔發起,最早在python2.5版本引入。如想了解更多關於PEP知識,可閱讀PEP入門指南),對上下文管理器給出如下定義:

上下文管理器是指提供了一對專門方法__enter __()和__exit __(),這些方法在with語句的主體進入和退出時被調用。在Python語言中添加了一個「 with」新語句,從而可以排除try / finally語句的標準用法。

也就是說,如果某個類定義了__enter__、__exit__方法,允許在語句體執行前進入該上下文、執行後退出該上下文,那麼這樣的類就可以稱作是上下文管理器對象。而且該用法可用作對try/finally的替代,以處理可能存在的異常。

前面舉了文件操作的with用法,說明文件操作的對象是一個上下文管理器對象,那麼我們先來看下文件操作類來怎樣定義的:

1##_pyio.py
2### Context manager ###
3def __enter__(self):  # That's a forward reference
4    """Context management protocol.  Returns self (an instance of IOBase)."""
5    self._checkClosed()
6    return self
7
8def __exit__(self, *args):
9    """Context management protocol.  Calls close()"""
10    self.close()

註:open()函數返回一個FileIO類對象,FileIO類繼承自RawIOBase類,RawIOBase又繼承自IOBase類,而IOBase類定義了__enter__和__exit__方法,因而是一個上下文管理器對象。同時,在IOBase類說明文檔中,也給出了這樣介紹:

1"""IOBase also supports the :keyword:`with` statement. In this example,
2fp is closed after the suite of the with statement is complete:
3
4with open('spam.txt', 'r') as fp:
5    fp.write('Spam and eggs!')"""

1from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
2def multi_thread():
3    with ThreadPoolExecutor() as executor:
4        return list(executor.map(is_prime, PRIMES))
5
6def multi_process():
7    with ProcessPoolExecutor() as executor:
8        return list(executor.map(is_prime, PRIMES))

那麼有理由相信,concurrent.futures類肯定也定義了__enter__和__exit__方法,具體如下:

1##_base.py
2def __enter__(self):
3    return self
4
5def __exit__(self, exc_type, exc_val, exc_tb):
6    self.shutdown(wait=True)
7    return False

註:ThreadPoolExecutor和ProcessPoolExecutor兩個類均繼承自Executor類,而Excutor類是一個上下文管理器對象。

需補充說明的是,正如上述示例代碼給出的那樣,__exit__()方法默認接收3個參數:exc_type、exc_val、exc_tb,如果with語句體發生異常,則3個參數分別賦值為type(error)、str(error)、error.__traceback__,否則均為None。

至此,我們可以得出這樣的結論:一個上下文管理器對象是指定義了__enter__和__exit__方法的類對象;反之,定義了__enter__和__exit__方法的類可以應用with包裝實現上下文管理器用法。

應用with包裝上下文管理器時:執行with語句生成一個實例對象時,自動調用__enter__方法創建一個適用於上下文環境的類對象,完畢後無論是否觸發異常都會調用__exit__方法執行退出操作。

實際上,上下文管理器常適用於帶有資源管理的操作,如前面例子中給出的文件操作和並發操作。這裡,我們舉一個操作資料庫的例子,實現一個簡單的打開資料庫類。

1import pymysql
2class OpenMySQL(object):
3    def __init__(self, db):
4        self.conn = pymysql.connect(host="localhost", user="root", password="123456", db=db, charset='utf8')
5
6    def __enter__(self):
7        return self.conn.cursor()
8
9    def __exit__(self, exc_type, exc_val, exc_tb):
10        self.conn.commit()
11        self.conn.close()
12
13if __name__ == '__main__':
14    with OpenMySQL('mytest') as sql:
15        sql_insert = 'insert into tbtest values (2);'
16        sql.execute(sql_insert)

代碼功能執行正常,且相比於基本的資料庫connect()--commit()--close()操作流程來說,要優雅許多。

註:寫完例子後查閱源碼發現pymysql.connect方法返回對象本身就是一個上下文管理器對象……。但僅僅用於舉例的話,這也足以說明with包裝上下文管理器的用法了。

本文對python中上下文管理器和with用法進行了簡單介紹,包括:

上下文管理器是一個實現了__enter__、__exit__魔法方法的類對象定義了__enter__、__exit__方法的類對象即可用作上下文管理器

上下文管理器通常使用with語句實現

with語句可實現簡潔版的try/finally替代用法

相關閱讀:

相關焦點

  • 有趣的Python上下文管理器
    這等價於:編寫自定義上下文管理器要編寫自定義上下文管理器,需要創建一個包含__enter__和從生成器到上下文管理器 上述通過使用__enter__和__exit__方法來編寫類僅僅是創建上下文管理器的途徑之一。另一個途徑是通過導入contextlib.contextmanager庫來實現。
  • python異常處理與上下文管理器
    異常處理異常與錯誤錯誤可以通過IDE或者解釋器給出提示的錯誤opentxt('a.jpg','r')語法層面沒有問題,但是自己代碼的邏輯有問題if age>18: print('未成年')異常多指在程序執行過程中,出現的未知錯誤,語法和邏輯本身是正確的。
  • Python--- 上下文管理器
    我們的第二段程序就使用了上下文管理器 (with...as...)。上下文管理器有隸屬於它的程序塊。當隸屬的程序塊執行結束的時候(也就是不再縮進),上下文管理器自動關閉了文件 (我們通過f.closed來查詢文件是否關閉)。我們相當於使用縮進規定了文件對象f的使用範圍。
  • Python的with語句與上下文管理器詳解
    二、上下文管理器原理f 對象之所以會自動執行自己的close方法,是因為它是一個上下文管理器,所以我們要先說說什麼是上下文管理器。上下文管理器是內部實現了__enter__和__exit__方法的對象(比如下面EXPR表達式獲取到的對象)它的一般使用方法是:with EXPR as VAR:    BLOCK上述代碼的執行過程等價於:ContextManager = EXPRVAR = ContextManager.
  • 深入了解​Python上下文管理器模塊--contextlib
    今天就讓我們一起學習 Python 中的上下文管理 contextlib。上下文管理器 上下文,簡而言之,就是程式所執行的環境狀態,或者說程式運行的情景。既然提及上下文,就不可避免的涉及 Python 中關於上下文的魔法。上下文管理器(context manager)是 Python2.5 開始支持的一種語法,用於規定某個對象的使用範圍。
  • 叮咚,我精心幫你總結了Python with上下文管理器的知識點,請查收!
    正好前幾天有小夥伴在「測試開發群」裡問「Python上下文管理器」有哪些使用場景。我感覺多數人應該經常用,但是換了個問法後就有點陌生了,所以我花點時間給大家整理下Python上下文管理器的知識點。功夫不負有心人啊,在IOBase中,我們看到了我們想找的代碼,如下所示:這個enter和exit方法又是怎麼回事呢?網上查了下,它們和Python With上下文管理器有關係。
  • Python協程:概念及其用法
    但這一過程並不是函數調用(沒有調用語句),這一整個過程看似像多線程,然而協程只有一個線程執行。優勢 執行效率極高,因為子程序切換(函數)不是線程切換,由程序自身控制,沒有切換線程的開銷。所以與多線程相比,線程的數量越多,協程性能的優勢越明顯。
  • 怎麼樣才算是精通 Python?
    點擊藍字「python
  • Python高級編程技巧
    這篇指南嘗試將大部分常用的數據結構知識放到一起,並且提供對其最佳用法的探討。而如果使用filter、lambda和map函數,則能夠將代碼大大簡化:嗯,這麼一來代碼就會在水平方向上展開。那麼是否能夠繼續簡化代碼呢?
  • Python 中的 With 語句
    在下一節中,我將檢查實現細節,並說明如何編寫用於此語句的對象。with 後面的表達式需支持上下文管理協議 (即,__enter__() 和__exit__() 方法)。decimal模塊中 的新 localcontext() 函數使保存和還原當前decimal上下文變得容易,它封裝了計算所需的精度和捨入特徵:                from decimal import Decimal, Context, localcontext# 顯示默認精度:28 位數字
  • Python 的 with 語句
    術語介紹一組與上下文管理器和with語句有關的概念。上下文管理協議(Context Management Protocol):包含方法__enter__()和__exit__(),支持該協議的對象要實現這兩個方法。
  • Python中的global關鍵字的用法以及如何設置變量?
    Python中的global關鍵字的用法global的英文相比你肯定知道,全球的,總的。
  • 說說Python中with的用法?
    公眾號新增加了一個欄目,就是每天給大家解答一道Python常見的面試題,反正每天不貪多,一天一題,正好合適,只希望這個面試欄目,給那些正在準備面試的同學
  • Python for循環及用法詳解
    for 循環的語法格式如下:for 迭代變量 in 字符串|列表|元組|字典|集合:    代碼塊格式中,迭代變量用於存放從序列類型變量中讀取出來的元素,所以一般不會在循環中對迭代變量手動賦值;代碼塊指的是具有相同縮進格式的多行代碼(和 while 一樣),由於和循環結構聯用,因此代碼塊又稱為循環體。
  • Python中必須知道的5對魔術方法
    下劃線在函數命名中的另一種用法是魔術方法(magic methods),也稱為特殊方法。具體來說,我們在函數名稱之前放置兩個下劃線,在函數名稱之後放置兩個下劃線-類似於 __func__。由於使用了雙下劃線,因此某些人將特殊方法稱為 「dunder方法」 或簡稱為 「dunders」 。在本文中,我想回顧五對緊密相關的常用魔術方法,每對方法代表一個 Python 概念。
  • python循環語句for和while用法-py猜數字小遊戲-學習python第4天
    這時我們如果採取老辦法,重複寫3600次print("python循環語句")是可以完成任務,但是這樣太枯燥、太乏味,作為程式設計師就應該學會變通,所以就出現了python循環語句,利用python循環語句,一小段代碼可以幫你解決重複輸出3600次的煩惱,可見python循環語句主要是用於解決某些重複的代碼工作。那麼有哪些是python循環語句呢?
  • Python小知識:如何編寫一個本地論文管理器?「西安IT培訓」
    西安IT培訓小編給大家講解下Python小知識,如何編寫一個本地論文管理器?1、介紹和引入最近初學 NLP 相關的深度學習,下了很多論文,數量一多,發現論文管理是個問題。首先論文數目一多,必須要按類別放到子文件夾下。但是某一篇論文,往往有多個主題。
  • python基礎-bytes和bytearray的用法
    Python中的序列類型有bytes和bytearray。二進位序列類型的用法比較少見,是python中少用的一種序列類型,對於二進位序列類型,大家基本了解即可。編制by偉森leung 喜歡本文章的可以請關注偉森leung,後續有更多python方面的知識分享。標籤: bytes、bytearray的用法, python教程
  • Python小知識:淺談Python的with語句
    with 語句適用於對資源進行訪問的場合,確保不管使用過程中是否發生異常都會執行必要的「清理」操作,釋放資源,比如文件使用後自動關閉、線程中鎖的自動獲取和釋放等。術語要使用 with 語句,首先要明白上下文管理器這一概念。有了上下文管理器,with 語句才能工作。下面是一組與上下文管理器和with 語句有關的概念。