日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

Python 中 with 用法详解

發布時間:2024/7/23 python 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Python 中 with 用法详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

?

淺談 Python 的 with 語句:https://developer.ibm.com/zh/articles/os-cn-pythonwith/

python3,淺談with的神奇魔法:https://blog.csdn.net/lxy210781/article/details/81176687

Python 的 with 語句詳解:https://www.jb51.net/article/51045.htm

深入理解 Python 的 With-as 語句:https://cloud.tencent.com/developer/article/1083148

python with statement 進階理解:https://www.iteye.com/blog/jianpx-505469

Python 中的with關鍵字使用詳解:https://www.jb51.net/article/92387.htm

?

?

由來

?

with…as 是 python 的控制流語句,像 if ,while一樣。with…as 語句是簡化版的 try except finally語句。

先理解一下 try…except…finally 語句是干啥的。實際上 try…except 語句和 try…finally 語句是兩種語句,用于不同的場景。但是當二者結合在一起時,可以“實現穩定性和靈活性更好的設計”。

?

1. try…except 語句

用于處理程序執行過程中的異常情況,比如語法錯誤、從未定義變量上取值等等,也就是一些python程序本身引發的異常、報錯。比如你在python下面輸入 1 / 0:

>>> 1/0 Traceback (most recent call last):File "<stdin>", line 1, in <module> ZeroDivisionError: division by zero

系統會給你一個 ZeroDivisionError 的報錯。說白了就是為了防止一些報錯影響你的程序繼續運行,就用try語句把它們抓出來(捕獲)。

try…except 的標準格式:

try: ## normal block except A: ## exc A block except: ## exc other block else: ## noError block

程序執行流程是:

–>執行normal block –>發現有A錯誤,執行 exc A block(即處理異常) –>結束如果沒有A錯誤呢? –>執行normal block –>發現B錯誤,開始尋找匹配B的異常處理方法,發現A,跳過,發現except others(即except:),執行exc other block –>結束如果沒有錯誤呢? –>執行normal block –>全程沒有錯誤,跳入else 執行noError block –>結束

Tips: 我們發現,一旦跳入了某條except語句,就會執行相應的異常處理方法(block),執行完畢就會結束。不會再返回try的normal block繼續執行了。

try:a = 1 / 2 #a normal number/variableprint(a)b = 1 / 0 # an abnormal number/variableprint(b)c = 2 / 1 # a normal number/variableprint(c) except:print("Error")

結果是,先打出了一個0,又打出了一個Error。就是把ZeroDivisionError錯誤捕獲了。

先執行 try 后面這一堆語句,由上至下:

  • step1: a 正常,打印a. 于是打印出0.5 (python3.x以后都輸出浮點數)
  • step2: b, 不正常了,0 不能做除數,所以這是一個錯誤。直接跳到except報錯去。于是打印了Error。
  • step3: 其實沒有step3,因為程序結束了。c是在錯誤發生之后的b語句后才出現,根本輪不到執行它。也就看不到打印出的c了

但這還不是 try/except 的所有用法

except后面還能跟表達式的!

所謂的表達式,就是錯誤的定義。也就是說,我們可以捕捉一些我們想要捕捉的異常。而不是什么異常都報出來。

異常分為兩類:

  • python標準異常
  • 自定義異常

我們先拋開自定義異常(因為涉及到類的概念),看看 except 都能捕捉到哪些 python 標準異常。請查看菜鳥筆記

https://www.runoob.com/python/python-exceptions.html

?

2. try…finallly 語句

用于無論執行過程中有沒有異常,都要執行清場工作。

try: execution block ##正常執行模塊 except A: exc A block ##發生A錯誤時執行 except B: exc B block ##發生B錯誤時執行 except: other block ##發生除了A,B錯誤以外的其他錯誤時執行 else: if no exception, jump to here ##沒有錯誤時執行 finally: final block ##總是執行

tips:?注意順序不能亂,否則會有語法錯誤。如果用 else 就必須有 except,否則會有語法錯誤。

try:a = 1 / 2print(a)print(m) # 拋出 NameError異常, 此后的語句都不在執行b = 1 / 0print(b)c = 2 / 1print(c) except NameError:print("Ops!!") # 捕獲到異常 except ZeroDivisionError:print("Wrong math!!") except:print("Error") else:print("No error! yeah!") finally: # 是否異常都執行該代碼塊print("Successfully!")

?

?

1.?with 語句的原理

?

  • 上下文管理協議(Context Management Protocol):包含方法 __enter__()__exit__(),支持該協議的對象要實現這兩個方法。
  • 上下文管理器(Context Manager):支持上下文管理協議的對象,這種對象實現了 __enter__() 和 __exit__() 方法。上下文管理器定義執行 with 語句時要建立的運行時上下文,負責執行 with 語句塊上下文中的進入與退出操作。通常使用 with 語句調用上下文管理器,也可以通過直接調用其方法來使用。

說完上面兩個概念,我們再從 with 語句的常用表達式入手,一段基本的 with 表達式,其結構是這樣的:

with context_expression [as target(s)]:...with-body...

其中 context_expression?可以是任意表達式;as target(s) 是可選的。

with 語句執行過程 。在語義上等價于:

context_manager = context_expressionexit = type(context_manager).__exit__value = type(context_manager).__enter__(context_manager)exc = True # True 表示正常執行,即便有異常也忽略;False 表示重新拋出異常,需要對異常進行處理try:try:target = value # 如果使用了 as 子句with-body # 執行 with-bodyexcept:# 執行過程中有異常發生exc = False# 如果 __exit__ 返回 True,則異常被忽略;如果返回 False,則重新拋出異常# 由外層代碼對異常進行處理if not exit(context_manager, *sys.exc_info()):raisefinally:# 正常退出,或者通過 statement-body 中的 break/continue/return 語句退出# 或者忽略異常退出if exc:exit(context_manager, None, None, None)# 缺省返回 None,None 在布爾上下文中看做是 False

可以看到,with 和 try finally 有下面的等價流程:

try: 執行 __enter__的內容 執行 with_block. finally: 執行 __exit__內容
  • 執行 context_expression ,生成上下文管理器 context_manager
  • 調用上下文管理器的 __enter__() 方法;如果使用了 as 子句,則將?__enter__() 方法的 返回值 賦值給 as 子句中的 target(s)
  • 執行語句體 with-body
  • 不管執行過程中是否發生了異常,執行上下文管理器的 __exit__() 方法,__exit__() 方法負責執行 "清理"?工作,如釋放資源等。如果執行過程中沒有出現異常,或者語句體中執行了語句 break/continue/return,則以 None 作為參數調用?__exit__(None, None, None) ;如果執行過程中出現異常,則使用 sys.excinfo 得到的異常信息為參數調用??__exit__(exc_type, exc_value, exc_traceback)
  • 出現異常時,如果?__exit__(type, value, traceback) 返回 False,則會重新拋出異常,讓 with 之外的語句邏輯來處理異常,這也是通用做法;如果返回 True,則忽略異常,不再對異常進行處理
  • ?

    那么__enter__和__exit__是怎么用的方法呢?我們直接來看一個栗子好了。

    程序無錯的例子:

    class Sample(object): # object類是所有類最終都會繼承的類def __enter__(self): # 類中函數第一個參數始終是self,表示創建的實例本身print("In __enter__()")return "Foo"def __exit__(self, type, value, trace):print("In __exit__()")def get_sample():return Sample()with get_sample() as sample:print("sample:", sample)print(Sample) # 這個表示類本身 <class '__main__.Sample'> print(Sample()) # 這表示創建了一個匿名實例對象 <__main__.Sample object at 0x00000259369CF550>''' In __enter__() sample: Foo In __exit__() <class '__main__.Sample'> <__main__.Sample object at 0x00000226EC5AF550>'''

    步驟分析:
    –> 調用get_sample()函數,返回Sample類的實例;
    –> 執行Sample類中的__enter__()方法,打印"In__enter_()"字符串,并將字符串“Foo”賦值給as后面的sample變量;
    –> 執行with-block碼塊,即打印"sample: %s"字符串,結果為"sample: Foo"
    –> 執行with-block碼塊結束,返回Sample類,執行類方法__exit__()。因為在執行with-block碼塊時并沒有錯誤返回,所以type,value,trace這三個arguments都沒有值。直接打印"In__exit__()"

    ?

    程序有錯的例子:

    class Sample:def __enter__(self):return selfdef __exit__(self, type, value, trace):print("type:", type)print("value:", value)print("trace:", trace)def do_something(self):bar = 1 / 0return bar + 10with Sample() as sample:sample.do_something()''' type: <class 'ZeroDivisionError'> value: division by zero trace: <traceback object at 0x0000019B73153848> Traceback (most recent call last):File "F:/機器學習/生物信息學/Code/first/hir.py", line 16, in <module>sample.do_something()File "F:/機器學習/生物信息學/Code/first/hir.py", line 11, in do_somethingbar = 1 / 0 ZeroDivisionError: division by zero '''

    步驟分析:
    –> 實例化Sample類,執行類方法__enter__(),返回值self也就是實例自己賦值給sample。即sample是Sample的一個實例(對象);
    –>執行with-block碼塊: 實例sample調用方法do_something();
    –>執行do_something()第一行?bar = 1 / 0,發現ZeroDivisionError,直接結束with-block代碼塊運行
    –>執行類方法__exit__(),帶入ZeroDivisionError的錯誤信息值,也就是type,value,?trace,并打印它們。

    ?

    如果有多個項目,則會視作存在多個?with?語句嵌套來處理多個上下文管理器:?(?https://docs.python.org/zh-cn/3/reference/compound_stmts.html#the-with-statement )

    with A() as a, B() as b:SUITE在語義上等價于:with A() as a:with B() as b:SUITE

    在 3.1 版更改:?支持多個上下文表達式。

    參見:PEP 343?- "with" 語句。Python?with?語句的規范描述、背景和示例。

    ?

    ?

    2.?自定義上下文管理器

    ?

    開發人員可以自定義支持上下文管理協議的類。自定義的上下文管理器要實現上下文管理協議所需要的?enter() 和?exit() 兩個方法:

    • contextmanager.__enter__()?:進入上下文管理器的運行時上下文,在語句體執行前調用。with 語句將該方法的返回值賦值給 as 子句中的 target,如果指定了 as 子句的話
    • contextmanager.__exit__(exc_type, exc_value, exc_traceback)?:退出與上下文管理器相關的運行時上下文,返回一個布爾值表示是否對發生的異常進行處理。參數表示引起退出操作的異常,如果退出時沒有發生異常,則3個參數都為None。如果發生異常時,返回True 表示不處理異常,否則會在退出該方法后重新拋出異常以由 with 語句之外的代碼邏輯進行處理。如果該方法內部產生異常,則會取代由 statement-body 中語句產生的異常。要處理異常時,不要顯示重新拋出異常,即不能重新拋出通過參數傳遞進來的異常,只需要將返回值設置為 False 就可以了。之后,上下文管理代碼會檢測是否?__exit__() 失敗來處理異常

    下面通過一個簡單的示例來演示如何構建自定義的上下文管理器。

    注意,上下文管理器必須同時提供?__enter__() 和?__exit__() 方法的定義,缺少任何一個都會導致 AttributeError;with 語句會先檢查是否提供了?__exit__() 方法,然后檢查是否定義了?__enter__() 方法。

    # coding = utf-8class DBManager(object):def __init__(self):passdef __enter__(self):print('__enter__')return selfdef __exit__(self, exc_type, exc_val, exc_tb):print('__exit__')return Truedef getInstance():return DBManager()with getInstance() as dbManagerIns:print('with demo')''' 運行結果: __enter__ with demo __exit__ '''

    with 后面必須跟一個上下文管理器,如果使用了 as,則是把上下文管理器的 __enter__() 方法的返回值賦值給 target,target 可以是單個變量,或者由 "()"?括起來的元組(不能是僅僅由 "," 分隔的變量列表,必須加 "()")

    結果分析:當我們使用 with 的時候,__enter__方法被調用,并且將返回值賦值給 as 后面的變量,并且在退出 with 的時候自動執行 __exit__ 方法

    class With_work(object):def __enter__(self):"""進入with語句的時候被調用"""print('enter called')return "xxt"def __exit__(self, exc_type, exc_val, exc_tb):"""離開with的時候被with調用"""print('exit called')with With_work() as as_f:print(f'as_f : {as_f}')print('hello with')''' enter called as_f : xxt hello with exit called '''

    ?

    示例 2:

    自定義支持 with 語句的對象?

    class DummyResource:def __init__(self, tag):self.tag = tagprint(f'Resource [{tag}]')def __enter__(self):print(f'[Enter {self.tag}]: Allocate resource.')return self # 可以返回不同的對象def __exit__(self, exc_type, exc_value, exc_tb):""":param exc_type: 錯誤的類型:param exc_value: 錯誤類型對應的值 :param exc_tb: 代碼中錯誤發生的位置 :return:"""print(f'[Exit {self.tag}]: Free resource.')if exc_tb is None:print(f'[Exit {self.tag}]: Exited without exception.')else:print(f'[Exit {self.tag}]: Exited with exception raised.')return False # 可以省略,缺省的None也是被看做是False# 第一個 with 語句 num = 50 print('*' * num) with DummyResource('First'):print('[with-body] Run without exceptions.') print('*' * num)# 第二個 with 語句 print('*' * num) with DummyResource('second'):print('[with-body] Run with exception.')raise Exceptionprint('[with-body] Run with exception. Failed to finish statement-body!') print('*' * num)# 嵌套 with 語句 print('*' * num) with DummyResource('Normal'):print('[with-body] Run without exceptions.')with DummyResource('With-Exception'):print('[with-body] Run with exception.')raise Exceptionprint('[with-body] Run with exception. Failed to finish statement-body!') print('*' * num)

    DummyResource 中的?__enter__() 返回的是自身的引用,這個引用可以賦值給 as 子句中的 target 變量;返回值的類型可以根據實際需要設置為不同的類型,不必是上下文管理器對象本身。

    __exit__() 方法中對變量 exctb 進行檢測,如果不為 None,表示發生了異常,返回 False 表示需要由外部代碼邏輯對異常進行處理;注意到如果沒有發生異常,缺省的返回值為 None,在布爾環境中也是被看做 False,但是由于沒有異常發生,__exit__() 的三個參數都為 None,上下文管理代碼可以檢測這種情況,做正常處理。

    執行結果:

    ************************************************** Resource [First] [Enter First]: Allocate resource. [with-body] Run without exceptions. [Exit First]: Free resource. [Exit First]: Exited without exception. ************************************************** ************************************************** Resource [second] [Enter second]: Allocate resource. [with-body] Run with exception. [Exit second]: Free resource. [Exit second]: Exited with exception raised. Traceback (most recent call last):File "temp.py", line 30, in <module>raise Exception Exception

    第1個 with? 語句執行結果:可以看到,正常執行時會先執行完語句體 with-body,然后執行?__exit__() 方法釋放資源。

    第2個 with 語句的執行結果:可以看到,with-body 中發生異常時with-body 并沒有執行完,但資源會保證被釋放掉,同時產生的異常由 with 語句之外的代碼邏輯來捕獲處理。

    因為第2個with語句發生異常,所以 嵌套 with 語句沒有執行。。。

    ?

    ?

    3. 自動關閉文件

    ?

    我們都知道打開文件有兩種方法:

    • f = open()
    • with open() as f:

    這兩種方法的區別就是第一種方法需要我們自己關閉文件;f.close(),而第二種方法不需要我們自己關閉文件,無論是否出現異常,with都會自動幫助我們關閉文件,這是為什么呢?

    我們先自定義一個類,用with來打開它:

    class Foo(object):def __enter__(self):print("enter called")def __exit__(self, exc_type, exc_val, exc_tb):print("exit called")print("exc_type :%s" % exc_type)print("exc_val :%s" % exc_val)print("exc_tb :%s" % exc_tb)with Foo() as foo:print("hello python")a = 1 / 0print("hello end")''' enter called Traceback (most recent call last): hello python exit called exc_type :<class 'ZeroDivisionError'> exc_val :division by zeroFile "F:/workspaces/python_workspaces/flask_study/with.py", line 25, in <module>a = 1/0 exc_tb :<traceback object at 0x0000023C4EDBB9C8> ZeroDivisionError: division by zeroProcess finished with exit code 1 '''

    執行結果的輸入順序,分析如下:

    當我們 with Foo() as foo: 時,此時會執行 __enter__方法,然后進入執行體,也就是:

    print("hello python") a = 1/0 print("hello end")

    語句,但是在 a=1/0 出現了異常,with將會中止,此時就執行__exit__方法,就算不出現異常,當執行體被執行完畢之后,__exit__方法仍然被執行一次。

    我們回到 with open("file")as f: 不用關閉文件的原因就是在 __exit__ 方法中,存在關閉文件的操作,所以不用我們手工關閉文件,with已將為我們做好了這個操作,這就可以理解了。

    ?

    ?

    4.?contextlib 模塊

    ?

    contextlib --- 為 with語句上下文提供的工具:https://docs.python.org/zh-cn/3/library/contextlib.html#contextlib.asynccontextmanager

    contextlib 模塊提供了3個對象,使用這些對象,可以對已有的生成器函數或者對象進行包裝,加入對上下文管理協議的支持,避免了專門編寫上下文管理器來支持 with 語句。

    • 裝飾器 contextmanager
    • 函數 nested?
    • 上下文管理器 closing

    ?

    裝飾器 contextmanager

    contextmanager 用于對生成器函數進行裝飾,生成器函數被裝飾以后,返回的是一個上下文管理器,其?enter() 和?exit() 方法由 contextmanager 負責提供,而不再是之前的迭代子。被裝飾的生成器函數只能產生一個值,否則會導致異常 RuntimeError;產生的值會賦值給 as 子句中的 target,如果使用了 as 子句的話。下面看一個簡單的例子。

    from contextlib import contextmanager@contextmanager def demo():print('[Allocate resources]')print('Code before yield-statement executes in __enter__')yield '*** contextmanager demo ***'print('Code after yield-statement executes in __exit__')print('[Free resources]')with demo() as value:print(f'Assigned Value: {value}')''' [Allocate resources] Code before yield-statement executes in __enter__ Assigned Value: *** contextmanager demo *** Code after yield-statement executes in __exit__ [Free resources] '''

    可以看到,生成器函數中 yield 之前的語句在?enter() 方法中執行,yield 之后的語句在?exit() 中執行,而 yield 產生的值賦給了 as 子句中的 value 變量。

    需要注意的是,contextmanager 只是省略了?enter() /?exit() 的編寫,但并不負責實現資源的”獲取”和”清理”工作;”獲取”操作需要定義在 yield 語句之前,”清理”操作需要定義 yield 語句之后,這樣 with 語句在執行?enter() /?exit() 方法時會執行這些語句以獲取/釋放資源,即生成器函數中需要實現必要的邏輯控制,包括資源訪問出現錯誤時拋出適當的異常。

    ?

    函數 nested

    nested 可以將多個上下文管理器組織在一起,避免使用嵌套 with 語句。

    nested 語法with nested(A(), B(), C()) as (X, Y, Z):# with-body code here類似于:with A() as X:with B() as Y:with C() as Z:# with-body code here需要注意的是,發生異常后,如果某個上下文管理器的 exit() 方法對異常處理返回 False, 則更外層的上下文管理器不會監測到異常。

    ?

    上下文管理器 closing

    closing 的實現如下:

    class closing(object):# help doc heredef __init__(self, thing):self.thing = thingdef __enter__(self):return self.thingdef __exit__(self, *exc_info):self.thing.close()

    上下文管理器會將包裝的對象賦值給 as 子句的 target 變量,同時保證打開的對象在 with-body 執行完后會關閉掉。closing 上下文管理器包裝起來的對象必須提供 close() 方法的定義,否則執行時會報 AttributeError 錯誤。

    自定義支持 closing 的對象

    from contextlib import closingclass ClosingDemo(object):def __init__(self):self.acquire()def acquire(self):print('Acquire resources.')def free(self):print('Clean up any resources acquired.')def close(self):self.free()with closing(ClosingDemo()):print('Using resources')''' Acquire resources. Using resources Clean up any resources acquired. '''

    closing 適用于提供了 close() 實現的對象,比如網絡連接、數據庫連接等,也可以在自定義類時通過接口 close() 來執行所需要的資源”清理”工作。

    ?

    ?

    5. 總結

    ?

    with 是對 try…expect…finally 語法的一種簡化,并且提供了對于異常非常好的處理方式。在Python有2種方式來實現 with 語法:class-based 和 decorator-based,2種方式在原理上是等價的,可以根據具體場景自己選擇。

    with 最初起源于一種block…as…的語法,但是這種語法被很多人所唾棄,最后誕生了with,關于這段歷史依然可以去參考PEP-343和PEP-340

    with 主要用在:自定義上下文管理器來對軟件系統中的資源進行管理,比如數據庫連接、共享資源的訪問控制等。文件操作。進程線程之間互斥對象。支持上下文其他對象

    ?

    ?

    ?

    總結

    以上是生活随笔為你收集整理的Python 中 with 用法详解的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。