成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

《Python有什么好學(xué)的》之上下文管理器

qpwoeiru96 / 1738人閱讀

摘要:引上下文管理器太極生兩儀,兩儀為陰陽。而最常用的則是,即上下文管理器使用上下文管理器用之后的文件讀寫會變成我們看到用了之后,代碼沒有了創(chuàng)建,也沒有了釋放。實(shí)現(xiàn)上下文管理器我們先感性地對進(jìn)行猜測。現(xiàn)實(shí)一個(gè)上下文管理器就是這么簡單。

“Python有什么好學(xué)的”這句話可不是反問句,而是問句哦。

主要是煎魚覺得太多的人覺得Python的語法較為簡單,寫出來的代碼只要符合邏輯,不需要太多的學(xué)習(xí)即可,即可從一門其他語言跳來用Python寫(當(dāng)然這樣是好事,誰都希望入門簡單)。

于是我便記錄一下,如果要學(xué)Python的話,到底有什么好學(xué)的。記錄一下Python有什么值得學(xué)的,對比其他語言有什么特別的地方,有什么樣的代碼寫出來更Pythonic。一路回味,一路學(xué)習(xí)。

引上下文管理器

太極生兩儀,兩儀為陰陽。

道有陰陽,月有陰晴,人有生死,門有開關(guān)。

你看這個(gè)門,它能開能關(guān),就像這個(gè)對象,它能創(chuàng)建能釋放。(扯遠(yuǎn)了

編程這行,幾十年來都繞不開內(nèi)存泄露這個(gè)問題。內(nèi)存泄露的根本原因,就是把某個(gè)對象創(chuàng)建了,但是卻沒有去釋放它。直到程序結(jié)束前那一刻,這個(gè)未被釋放的對象還一直占著內(nèi)存,即使程序已經(jīng)不用這個(gè)對象了。泄露的量少的話還好,量大的話就直接打滿內(nèi)存,然后程序就被kill了。

聰明的程序員經(jīng)過了這十幾年的努力,創(chuàng)造出很多高級編程語言,這些編程語言已經(jīng)不再需要讓程序員過度關(guān)注內(nèi)存的問題了。但是在編程時(shí),一些常見的對象釋放、流關(guān)閉還是要程序員顯式地寫出來。

最常見的就是文件操作了。

常見的文件操作方式

原始的Python文件操作方式,很簡單,也很common(也很java):

def read_file_1():
    f = open("file_demo.py", "r")
    try:
        print(f.read())
    except Exception as e:
        pass
    f.close()

就是這么簡簡單單的,先open然后讀寫再close,中間讀寫加個(gè)異常處理。

其中close就是釋放資源了,在這里如果不close,可能:

資源不釋放,直到不可控的垃圾回收來了,甚至直到程序結(jié)束

中間對文件修改時(shí),修改的信息還沒來得及寫入文件

整個(gè)代碼顯得不規(guī)范

因此寫上close函數(shù)理論上已經(jīng)必須的了,可是xxx.close()這樣寫上去,在邏輯復(fù)雜的時(shí)候讓人容易遺漏,同時(shí)也顯得不雅觀。

這時(shí),各種語言生態(tài)有各種解決方案。

像Java,就直接jvm+依賴注入,直接把對象的生命周期管理接管了,只留下對象的使用功能給程序員;像golang,defer一下就好。而python最常用的則是with,即上下文管理器

使用上下文管理器

用with之后的文件讀寫會變成:

def read_file_2():
    with open("file_demo.py", "r") as f:
        print(f.read())

我們看到用了with之后,代碼沒有了open創(chuàng)建,也沒有了close釋放。而且也沒有了異常處理,這樣子我們一看到代碼,難免會懷疑它的健壯性。

為了更好地理解上下文管理器,我們先實(shí)現(xiàn)試試。

實(shí)現(xiàn)上下文管理器

我們先感性地對with進(jìn)行猜測。

從調(diào)用with的形式上看,with像是一個(gè)函數(shù),包裹住了open和close:

# 大概意思而已 with = open + do + close
def with():
    open(xxxx)
    doSomething(xxxx)
    close(xxxx)

而Python的庫中已有的方案(contextmanager)也和上面的偽代碼具有一定的相似性:

from contextlib import contextmanager

@contextmanager
def c(s):
    print(s + "start")
    yield s
    print(s + "end")

“打印start”相當(dāng)于open,而“打印end”相當(dāng)于close,yield語法和修飾器(@)不熟悉的同學(xué)可以復(fù)習(xí)一下這些文章:生成器和修飾器。

然后我們調(diào)用這個(gè)上下文管理器試試,注意煎魚還給上下文管理器加了參數(shù)s,輸出的時(shí)候會帶上:

def test_context():
    with c("123") as cc:
        print("in with")
        print(type(cc))

if __name__ == "__main__":
    test_context()

我們看到,start和end前都有實(shí)參s=123。

現(xiàn)實(shí)一個(gè)上下文管理器就是這么簡單。

異常處理

但是我們必須要注重異常處理,假如上面的上下文管理器中拋異常了怎么辦呢:

def test_context():
    with c("123") as cc:
        print("in with")
        print(type(cc))
        raise Exception

結(jié)果:

顯然,這樣弱雞的異常處理,煎魚時(shí)忍不了的。而且最重要的是,后面的close釋放居然沒有執(zhí)行!

我們可以在實(shí)現(xiàn)上下管理器時(shí),接入異常處理:

@contextmanager
def c():
    print("start")
    try:
        yield
    finally:
        print("end")
        
def test_except():
    try:
        with c() as cc:
            print("in with")
            raise Exception

    except:
        print("catch except")

調(diào)用test_except函數(shù)輸出:

我們在上下文管理器的實(shí)現(xiàn)中加入了try-finally,保證出現(xiàn)異常的時(shí)候,上下文管理器也能執(zhí)行close。同時(shí)在調(diào)用with前也加入try結(jié)構(gòu),保證整個(gè)函數(shù)的正常運(yùn)行。

然而,加入了這些東西之后,整個(gè)函數(shù)變得復(fù)雜又難看。

因此,煎魚覺得,想要代碼好看,抽象的邏輯需要再次升華,即從函數(shù)的層面升為對象(類)的層面。

實(shí)現(xiàn)上下文管理器類

其實(shí)用類實(shí)現(xiàn)上下文管理器,從邏輯理解上簡單了很多,而且不需要引入那一個(gè)庫:

class ContextClass(object):
    def __init__(self, s):
        self.s = s

    def __enter__(self):
        print(self.s + "call enter")
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print(self.s + "call exit")

    def test(self):
        print(self.s + "call test")

從代碼的字面意思上,我們就能感受得出來,__enter__即為我們理解的open函數(shù),__exit__就是close函數(shù)。

接下來,我們調(diào)用一下這個(gè)上下文管理器:

def test_context():
    with ContextClass("123") as c:
        print("in with")
        c.test()
        print(type(c))
        print(isinstance(c, ContextClass))

    print("")
    c = ContextClass("123")
    print(type(c))
    print(isinstance(c, ContextClass))

if __name__ == "__main__":
    test_context()

輸出結(jié)果:

功能上和直接用修飾器一致,只是在實(shí)現(xiàn)的過程中,邏輯更清晰了。

異常處理

回到我們原來的話題:異常處理。

直接用修飾器實(shí)現(xiàn)的上下文管理器處理異常時(shí)可以說是很難看了,那么我們的類選手表現(xiàn)又如何呢?

為了方便比較,煎魚把未進(jìn)行異常處理的和已進(jìn)行異常處理的一起寫出來,然后煎魚調(diào)用一個(gè)不存在的方法來拋異常:

class ContextClass(object):
    def __init__(self, s):
        self.s = s

    def __enter__(self):
        print(self.s + "call enter")
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print(self.s + "call exit")

class ContextExceptionClass(object):
    def __init__(self, s):
        self.s = s

    def __enter__(self):
        print(self.s + "call enter")
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print(self.s + "call exit")
        return True
        
def test_context():
    with ContextExceptionClass("123") as c:
        print("in with")
        t = c.test()
        print(type(t))

    # with ContextClass("456") as c:
        # print("in with")
        # t = c.test()
        # print(type(t))

if __name__ == "__main__":
    test_context()

輸出不一樣的結(jié)果:

結(jié)果發(fā)現(xiàn),看了半天,兩個(gè)類只有最后一句不一樣:異常處理的類中__exit__函數(shù)多一句返回,而且還是return了True。

而且這兩個(gè)類都完成了open和close兩部,即使后者拋異常了。

而在__exit__中加return True的意思就是不把異常拋出。

如果想要詳細(xì)地處理異常,而不是像上面治標(biāo)不治本的隱藏異常,則需要在__exit__函數(shù)中處理異常即可,因?yàn)樵摵瘮?shù)中有著異常的信息。

不信?稍微再改改:

class ContextExceptionClass(object):
    def __init__(self, s):
        self.s = s

    def __enter__(self):
        print(self.s + "call enter")
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print(self.s + "call exit")
        print(str(exc_type) + " " + str(exc_val) + " " + str(exc_tb))
        return True

輸出與預(yù)期異常信息一致:

先這樣吧

若有錯誤之處請指出,更多地請關(guān)注造殼。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/45020.html

相關(guān)文章

  • Python什么好學(xué)修飾

    摘要:然后煎魚加了一個(gè)后再調(diào)用函數(shù),得到的輸出結(jié)果和加修飾器的一樣,換言之等效于因此,我們對于,可以理解是,它通過閉包的方式把新函數(shù)的引用賦值給了原來函數(shù)的引用。 Python有什么好學(xué)的這句話可不是反問句,而是問句哦。 主要是煎魚覺得太多的人覺得Python的語法較為簡單,寫出來的代碼只要符合邏輯,不需要太多的學(xué)習(xí)即可,即可從一門其他語言跳來用Python寫(當(dāng)然這樣是好事,誰都希望入門簡...

    lewinlee 評論0 收藏0
  • Python什么好學(xué)生成/迭代

    摘要:為什么是斐波那契談到生成器迭代器,人們總是喜歡用斐波那契數(shù)列來舉例。那么,換句話來說,即能由推導(dǎo)式得出的數(shù)列,其實(shí)都可以用來做生成器迭代器的例子。然而,生成器和生成器類的實(shí)例都屬于迭代器。 Python有什么好學(xué)的這句話可不是反問句,而是問句哦。 主要是煎魚覺得太多的人覺得Python的語法較為簡單,寫出來的代碼只要符合邏輯,不需要太多的學(xué)習(xí)即可,即可從一門其他語言跳來用Python寫...

    n7then 評論0 收藏0
  • 假裝Python高手,把類這樣改,真刺激!

    摘要:今天我們來說一個(gè)非常實(shí)用的例子,小菜接到組長老王的一個(gè)任務(wù),安排一個(gè)新的活,這個(gè)活是這樣的老王小菜啊,你幫我寫一個(gè)登入腳本,跑十幾條命令到服務(wù)器上,然后存一下日志。這個(gè)時(shí)候,小菜偷偷的瞄了一眼組長老王,常舒一口氣,總于寫完了。 Python學(xué)了好幾年,發(fā)現(xiàn)功力還是那樣,很多同學(xué)經(jīng)常這樣抱...

    dance 評論0 收藏0
  • Python下文管理和else塊

    摘要:上下文管理器協(xié)議包含和兩個(gè)方法。因此必要時(shí)在上下文管理器函數(shù)中使用語句防范錯誤。構(gòu)建臨時(shí)忽略指定異常的上下文管理器。這是個(gè)基類,用于定義基于類的上下文管理器。塊結(jié)束時(shí),按照后進(jìn)先出的順序調(diào)用棧中各個(gè)上下文管理器的方法。 導(dǎo)語:本文章記錄了本人在學(xué)習(xí)Python基礎(chǔ)之控制流程篇的重點(diǎn)知識及個(gè)人心得,打算入門Python的朋友們可以來一起學(xué)習(xí)并交流。 本文重點(diǎn): 1、掌握if語句之外的el...

    Michael_Lin 評論0 收藏0
  • 后端技術(shù) - 收藏集 - 掘金

    摘要:理解迭代對象迭代器生成器后端掘金本文源自作者的一篇博文,原文是,俺寫的這篇文章是按照自己的理解做的參考翻譯。比較的是兩個(gè)對象的內(nèi)容是后端掘金黑魔法之協(xié)程異步后端掘金本文為作者原創(chuàng),轉(zhuǎn)載請先與作者聯(lián)系。 完全理解關(guān)鍵字with與上下文管理器 - 掘金如果你有閱讀源碼的習(xí)慣,可能會看到一些優(yōu)秀的代碼經(jīng)常出現(xiàn)帶有 with 關(guān)鍵字的語句,它通常用在什么場景呢?今天就來說說 with 和 上下...

    oujie 評論0 收藏0

發(fā)表評論

0條評論

閱讀需要支付1元查看
<