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

Python上下文管理器詳細(xì)使用教程

 更新時(shí)間:2023年02月08日 14:01:12   作者:lijiachang8  
Python有三大神器,一個(gè)是裝飾器,一個(gè)是迭代器、生成器,最后一個(gè)就是今天文章的主角 -- 「上下文管理器」。上下文管理器在日常開發(fā)中的作用是非常大的,可能有些人用到了也沒有意識(shí)到這一點(diǎn)

with語句會(huì)設(shè)置一個(gè)臨時(shí)的上下文,交給上下文管理器對(duì)象控制,并且負(fù)責(zé)清理上下問題。

這樣做能避免錯(cuò)誤并減少樣板代碼,因此API能更安全,更易使用。除了自動(dòng)關(guān)閉文件之外,with塊還有很多用途。

上下文管理器和with塊

上下文管理器對(duì)象目的是管理with語句,就像迭代器的存在是為了管理for語句一樣。

with語句的目的是簡化try/finnally模式。

這種模式用于保證一段代碼運(yùn)行完畢后執(zhí)行某項(xiàng)操作,即便那段代碼是由于異常、return語句、sys.exit()調(diào)用而終止的,也都會(huì)執(zhí)行指定的finally操作。finally子句中通常存放用于釋放重要資源,或者還原臨時(shí)變更的狀態(tài)。

上下文管理器協(xié)議包含__enter__和__exit__兩個(gè)方法。

with語句開始運(yùn)行時(shí),會(huì)在上下文管理對(duì)象上調(diào)用__enter__方法;with語句運(yùn)行結(jié)束之后,會(huì)在上下文管理對(duì)象上調(diào)用__exit__方法,以扮演finally子句的角色。

示例,把文件對(duì)象當(dāng)做上下文管理器對(duì)象使用。

with open('cafe.txt') as fp:
    src = fp.read(60)
print(fp)  # fp變量依舊可以用
print(fp.closed, fp.encoding)  # 讀取fp對(duì)象的屬性
print(fp.read())  # 但是執(zhí)行fp的IO操作會(huì)異常

打印
<_io.TextIOWrapper name='cafe.txt' mode='r' encoding='cp936'>
True cp936
Traceback (most recent call last):
  File "C:/Users/lijiachang/PycharmProjects/collect_demo/test2.py", line 8, in <module>
    print(fp.read())
ValueError: I/O operation on closed file.

知識(shí)點(diǎn):

  • fp變量在上下文管理器之外,依舊存在,可以讀取fp對(duì)象屬性。因?yàn)閣ith塊于函數(shù)和模塊不同,沒有定義新的作用域。
  • 但是 不能在fp上再執(zhí)行IO操作,因?yàn)樵趙ith塊的末尾,已經(jīng)調(diào)用了TextIOWrapper__exit__方法把文件關(guān)閉了。

執(zhí)行with后面的表達(dá)式的結(jié)果是上下文管理器對(duì)象,不過,把值綁定到目標(biāo)變量(as后的變量)是在上下文管理器對(duì)象上調(diào)用__enter__方法的結(jié)果。

不管控制流程以哪種方式退出with塊,都會(huì)在上下文管理器對(duì)象上調(diào)用__exit__方法,而不是在__enter__方法返回的對(duì)象上調(diào)用。

with語句的as子句是可選的。對(duì)于像open這樣的函數(shù)來說,必須加上as子句,以便獲取文件的對(duì)象引用。不過一些上下文管理器對(duì)象會(huì)返回None,因?yàn)闆]有什么有用的對(duì)象給用戶提供。

示例,實(shí)現(xiàn)一個(gè)LookingGlass類,上下文管理器

class LookingGlass:
    """鏡子:看到的字是反的"""
    def __enter__(self):
        import sys
        self.original_write = sys.stdout.write  # 把原始的[屏幕打印輸出]函數(shù)保存到一個(gè)實(shí)例屬性中,供以后使用
        sys.stdout.write = self.reverse_write  # 猴子補(bǔ)?。禾鎿Q成自己的方法實(shí)現(xiàn)
        return "ABCD"
    def reverse_write(self, text):
        self.original_write(text[::-1])  # 調(diào)用原始的屏幕打印,但是把內(nèi)容反轉(zhuǎn)
    def __exit__(self, exc_type, exc_val, exc_tb):
        import sys
        sys.stdout.write = self.original_write  # 還原成原始的函數(shù)功能
        if exc_type is ZeroDivisionError:
            print('Do not divide by Zero!')
            return True  # 告訴解釋器,異常已經(jīng)處理
        # 其他的情況返回None,交給Python拋出異常
with LookingGlass() as what:
    print('lijiachang')
    print(what)
print(what)
print('back to normal')

打印
gnahcaijil
DCBA
ABCD
back to normal

知識(shí)點(diǎn):

  • sys.stdout.write 是標(biāo)準(zhǔn)屏幕打印輸出。要注意如果暫時(shí)緩存其他對(duì)象改變功能,記得最后還原成原來的版本。
  • Python調(diào)用__enter__方法時(shí),除了self之外不會(huì)傳入其他參數(shù)
  • 如果一切正常,Python調(diào)用__exit__方法時(shí),傳入的是None,None,None;如果拋出了異常,這三個(gè)參數(shù)是異常數(shù)據(jù)。如下:

exec_type: 異常類名稱。如ZeroDivisionError

exc_value: 異常實(shí)例。有時(shí)會(huì)有參數(shù)傳遞給異常構(gòu)造方法,例如錯(cuò)誤信息,這些參數(shù)使用exc_value.args獲取

traceback: traceback對(duì)象。

補(bǔ)充,在try/finally語句的finally塊中調(diào)用sys.exc_info()得到的就是__exit__接收的這三個(gè)參數(shù)。

  • 在__exit__中返回True是告訴解釋器,異常已經(jīng)處理了。如果返回True之外的值,比如默認(rèn)的None,with塊中的異常會(huì)向上冒泡,讓Python來拋出。

In [44]: manager = LookingGlass()

In [45]: manager

Out[45]: <__main__.LookingGlass at 0xac6a970>

In [46]: m = manager.__enter__()

In [47]: m == "ABCD"

Out[47]: eurT

In [49]: m

Out[49]: 'DCBA'

In [50]: manager

Out[50]: >079a6cax0 ta ssalGgnikooL.__niam__<

In [54]: manager.__exit__(None, None, None)

In [55]: m

Out[55]: 'ABCD'

可以看到在調(diào)用__enter__之后,所有的標(biāo)準(zhǔn)打印輸出,都會(huì)反轉(zhuǎn)。因?yàn)閟tdout的所有輸出都經(jīng)過了__enter__方法中打補(bǔ)丁的reverse_write方法實(shí)現(xiàn)。

contextlib模塊

Python標(biāo)準(zhǔn)庫文檔中的contextlib模塊,提供了自定義上下文管理器的一些函數(shù)

  • closing :如果對(duì)象提供了close()方法,但沒有實(shí)現(xiàn)__enter__/__exit__方法,可以使用這個(gè)函數(shù)來構(gòu)建上下文管理器。
  • suppress : 構(gòu)建臨時(shí)忽略指定異常的上下文。
  • @contextmanager :這個(gè)裝飾器可以把簡單的生成器變?yōu)樯舷挛墓芾砥?,這樣就不需要?jiǎng)?chuàng)建類來實(shí)現(xiàn)管理器協(xié)議了。
  • ContextDecorate :這是個(gè)基類,用于編寫類可以繼承他,用于定義基于類的上下文管理器。也可以用于裝飾器函數(shù),在受管理的上下文中運(yùn)行整個(gè)函數(shù)。
  • ExitStack : 這個(gè)上下文管理器能進(jìn)入多個(gè)上下文管理器。with塊結(jié)束時(shí),按照后進(jìn)先出的順序調(diào)用棧中各個(gè)上下文管理器的__exit__方法。如果事先不知道 with塊要進(jìn)入多少個(gè)上下文管理器,可以使用這個(gè)類。

使用最廣泛的還是@contextmanager裝飾器。要注意,這個(gè)裝飾器與迭代無關(guān),卻要使用yeild關(guān)鍵字。

@contextmanager 裝飾器

使用@contextmanager裝飾器呢個(gè)減少創(chuàng)建上下文管理器的代碼量,因?yàn)椴挥镁帉懸粋€(gè)完整的類,不用定義__enter__和__exit__方法,只需要一個(gè)實(shí)現(xiàn)yeild語句的生成器,生成想讓__enter__方法返回的值。

其中yeild語句的作用是把函數(shù)的定義體分為兩部分:

yeild語句前面的代碼在with塊開始時(shí)(即解釋器調(diào)用__enter__方法時(shí))執(zhí)行。

yeild語句后面的代碼在with塊結(jié)束時(shí)(即調(diào)用__exit__方法時(shí))執(zhí)行。

示例,使用生成器實(shí)現(xiàn)上下文管理器

import contextlib
@contextlib.contextmanager
def looking_glass():
    import sys
    original_write = sys.stdout.write  # 把原始的[屏幕打印輸出]函數(shù)保存到一個(gè)實(shí)例屬性中,供以后使用
    def reverse_write(text):
        original_write(text[::-1])
    sys.stdout.write = reverse_write  # 猴子補(bǔ)丁:替換成自己的方法實(shí)現(xiàn)
    yield "ABCD"  # 這個(gè)值會(huì)綁定到as后的變量上
    sys.stdout.write = original_write
with looking_glass() as what:
    print('lijiachang')
    print(what)
print(what)
print('back to normal')

知識(shí)點(diǎn) :

  • yield 后面的值會(huì)綁定到with語句中as子句的目標(biāo)變量上,執(zhí)行with塊中的代碼,這個(gè)函數(shù)會(huì)在這里暫停。
  • 控制權(quán)一旦調(diào)成with塊,就會(huì)繼續(xù)執(zhí)行yeild語句后的代碼

@contextmanager 原理和注意事項(xiàng)

其實(shí),contextlib.contextmanager裝飾器會(huì)把函數(shù)包裝實(shí)現(xiàn)成__enter__和__exit__方法的類。(ps:類的名字叫_GeneratorContextManager)

這個(gè)類的__enter__方法有如下作用:

  • 調(diào)用生成器函數(shù),保存生成器對(duì)象(這里把他稱為gen)。
  • 調(diào)用next(gen),執(zhí)行到y(tǒng)eild關(guān)鍵字所在的位置。
  • 返回next(gen)產(chǎn)出的值,把產(chǎn)出的值綁定到with/as語句的目標(biāo)變量上。

with塊終止時(shí),__exit__方法會(huì)做以下事情:

  • 檢查有沒有異常傳給exc_type:
  • 如果有,就調(diào)用gen.throw(exception), 在生成器函數(shù)定義體中包含yeild關(guān)鍵字的那一行拋出異常。
  • 如果沒有異常,再次調(diào)用next(gen),繼續(xù)執(zhí)行定義體中yeild語句之后的代碼。

在上面的示例中,有一個(gè)嚴(yán)重的問題:如果在with塊中拋出了異常,Python解釋器會(huì)捕獲,然后在looking_glass函數(shù)的yeild表達(dá)式再次拋出。但是問題是沒有處理錯(cuò)誤的代碼,那么looking_glass函數(shù)就會(huì)終止,永遠(yuǎn)的無法恢復(fù)成sys.stdout.write方法原始個(gè)功能,導(dǎo)致系統(tǒng)的輸出處于無效狀態(tài)。

所以要添加一下異常的處理,比如ZeroDivisionError異常。

示例,添加異常處理的基于生成器的上下文管理器

import contextlib
@contextlib.contextmanager
def looking_glass():
    """鏡子:看到的字是反的"""
    import sys
    original_write = sys.stdout.write  # 把原始的[屏幕打印輸出]函數(shù)保存到一個(gè)實(shí)例屬性中,供以后使用
    def reverse_write(text):
        original_write(text[::-1])
    sys.stdout.write = reverse_write  # 猴子補(bǔ)?。禾鎿Q成自己的方法實(shí)現(xiàn)
    msg = ''
    try:
        yield "ABCD"  # 只需要捕獲yield部分
    except ZeroDivisionError:
        msg = 'do not divide by zero'
    finally:
        sys.stdout.write = original_write
        if msg:
            print(msg)
with looking_glass() as what:
    0 / 0  # 拋出異常
    print('lijiachang')
    print(what)
print(what)
print('back to normal')

打印
do not divide by zero
ABCD
back to normal

知識(shí)點(diǎn):

  • 在生成器函數(shù)中只需要捕獲yeild關(guān)鍵字這行的異常,Python解釋器會(huì)在with塊中的異常,轉(zhuǎn)移到y(tǒng)ield這行拋出。
  • 所以在使用@contextmanager裝飾器時(shí),要把yeild語句放到try/finally語句中,因?yàn)槲覀冇肋h(yuǎn)不知道上下文管理器的用戶會(huì)在with塊中做什么。

關(guān)于異常的處理的對(duì)比:

  • 在用類實(shí)現(xiàn)上下文管理器時(shí),前面說過,為了告訴解釋器異常已經(jīng)處理過了,需要在__exit__方法中返回True,此時(shí)解釋器會(huì)壓制異常。如果__exit__沒有顯示的返回一個(gè)值,那么解釋器得到的就是None,此時(shí)會(huì)向上冒泡異常。
  • 在用@contextmanager裝飾器時(shí),默認(rèn)的行為是相反的:裝飾器提供的__exit__方法假定發(fā)給生成器的所有異常都已經(jīng)處理了,因此默認(rèn)壓制異常。如果不想讓contextmanager壓制異常,必須在裝飾的函數(shù)中顯式的重新拋出異常。

最后,再次強(qiáng)調(diào):在@contextmanager裝飾器裝飾去生成器中,yield與迭代沒有任何關(guān)系。

到此這篇關(guān)于Python上下文管理器詳細(xì)使用教程的文章就介紹到這了,更多相關(guān)Python上下文管理器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 淺析Python 讀取圖像文件的性能對(duì)比

    淺析Python 讀取圖像文件的性能對(duì)比

    這篇文章主要介紹了淺析Python 讀取圖像文件的性能對(duì)比,主要介紹了3種性能對(duì)比方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-03-03
  • python實(shí)現(xiàn)日歷效果

    python實(shí)現(xiàn)日歷效果

    這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)日歷效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • Python構(gòu)造函數(shù)與析構(gòu)函數(shù)超詳細(xì)分析

    Python構(gòu)造函數(shù)與析構(gòu)函數(shù)超詳細(xì)分析

    在python之中定義一個(gè)類的時(shí)候會(huì)在類中創(chuàng)建一個(gè)名為__init__的函數(shù),這個(gè)函數(shù)就叫做構(gòu)造函數(shù)。它的作用就是在實(shí)例化類的時(shí)候去自動(dòng)的定義一些屬性和方法的值,而析構(gòu)函數(shù)恰恰是一個(gè)和它相反的函數(shù),這篇文章主要介紹了Python構(gòu)造函數(shù)與析構(gòu)函數(shù)
    2022-11-11
  • Windows下Pycharm遠(yuǎn)程連接虛擬機(jī)中Centos下的Python環(huán)境(圖文教程詳解)

    Windows下Pycharm遠(yuǎn)程連接虛擬機(jī)中Centos下的Python環(huán)境(圖文教程詳解)

    由于最近學(xué)習(xí)tensorflow的需要,tensorflow是在Linux環(huán)境下,使用的是Python。為了方便程序的調(diào)試,嘗試在Windows下的Pycharm遠(yuǎn)程連接到虛擬機(jī)中Centos下的Python環(huán)境,感興趣的朋友跟隨小編看看吧
    2020-03-03
  • 詳細(xì)介紹pandas的DataFrame的append方法使用

    詳細(xì)介紹pandas的DataFrame的append方法使用

    這篇文章主要介紹了詳細(xì)介紹pandas的DataFrame的append方法使用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07
  • python?包實(shí)現(xiàn)JSON?輕量數(shù)據(jù)操作

    python?包實(shí)現(xiàn)JSON?輕量數(shù)據(jù)操作

    這篇文章主要介紹了python?包實(shí)現(xiàn)JSON?輕量數(shù)據(jù)操作,文章介紹內(nèi)容首先將對(duì)象轉(zhuǎn)為json字符串展開主題詳細(xì)內(nèi)容需要的小伙伴可以參考一下
    2022-04-04
  • python 開心網(wǎng)和豆瓣日記爬取的小爬蟲

    python 開心網(wǎng)和豆瓣日記爬取的小爬蟲

    我本科有個(gè)很幽默風(fēng)趣的量子力學(xué)老師,他說了很多批話,跟個(gè)公知似的。他的很多文章都放在了開心網(wǎng)(kaixin001.com)上,為了留個(gè)紀(jì)念,用爬蟲保存下來
    2021-05-05
  • Cython 三分鐘入門教程

    Cython 三分鐘入門教程

    根據(jù)一些我收到的反饋,大家似乎有點(diǎn)混淆——Cython是用來生成 C 擴(kuò)展到而不是獨(dú)立的程序的。所有的加速都是針對(duì)一個(gè)已經(jīng)存在的 Python 應(yīng)用的一個(gè)函數(shù)進(jìn)行的。
    2009-09-09
  • Python 在OpenCV里實(shí)現(xiàn)仿射變換—坐標(biāo)變換效果

    Python 在OpenCV里實(shí)現(xiàn)仿射變換—坐標(biāo)變換效果

    這篇文章主要介紹了Python 在OpenCV里實(shí)現(xiàn)仿射變換—坐標(biāo)變換效果,本文通過一個(gè)例子給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-08-08
  • Python讀取圖片的方法詳解

    Python讀取圖片的方法詳解

    這篇文章主要為大家詳細(xì)介紹了Python中讀取圖片的實(shí)現(xiàn)方法,文中的示例代碼簡潔易懂,具有一定的參考價(jià)值,需要的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-08-08

最新評(píng)論