python3中with語句的用法

python3中with語句用法

  • (一)背景-用於文件處理
在實際的編碼過程中,有時有一些任務,需要事先做一些設置,事後做一些清理,這時就需要python3 with出場了,with能夠對這樣的需求進行一個比較優雅的處理,最常用的例子就是對訪問文件的處理。
  • 1 初級文件處理
1 f = open(r'c:\mytest.txt', 'r')
2 data = f.read()
3 f.close()

存在兩個問題:
1. 如果在讀寫時出現異常而忘了異常處理。
2. 忘了關閉文件句柄
  • 2 初級文件處理
1  f = open(r'c:\mytest.txt', 'r') 
2 try: 
3     data = f.read() 
4 finally: 
5     f.close()

以上的寫法就可以避免因讀取文件時異常的發生而沒有關閉問題的處理了。代碼長了一些。
  • 3 高級文件處理
1 with open(r'c:\test.txt', 'r') as f:
2     data = f.read()

with後面接的對象返回的結果賦值給f。此例當中open函數返回的文件對象賦值給了f;with會自已獲取上下文件的異常信息。
  • (二)with語句的工作原理
__enter__()/__exit__()這兩個方法

with後面返回的對象要求必須有這兩個方法,而文件對象f剛好是有這兩個方法的。

object.__enter__(self)
進入與此對象相關的運行時上下文。with語句將將此方法的返回值綁定到語句的AS子句中指定的目標(如果有設置的話)

object.__exit__(self, exc_type, exc_value, traceback)
退出與此對象相關的運行時上下文。參數描述導致上下文退出的異常。如果上下文運行時沒有異常發生,那麼三個參數都將置爲None。
如果有異常發生,並且該方法希望抑制異常(即阻止它被傳播),則它應該返回True。否則,異常將在退出該方法時正常處理。

注意:
 __exit__()方法不應該重新拋出傳入的異常,這是調用者的職責。
  • 1 無異常情況
class Test:
    def __enter__(self):
        print('__enter__() is call!')
        return self
 
    def dosomething(self):
        print('dosomethong!')
 
    def __exit__(self, exc_type, exc_value, traceback):
        print('__exit__() is call!')
        print(f'type:{exc_type}')
        print(f'value:{exc_value}')
        print(f'trace:{traceback}')
        print('__exit()__ is call!')
 
with Test() as sample:
    sample.dosomething()
 

>>>__enter__() is call!
>>>dosomethong!
>>>__exit__() is call!
>>>type:None
>>>value:None
>>>trace:None
>>>__exit()__ is call!

以上的實例Text,我們注意到他帶有__enter__()/__exit__()這兩個方法,當對象被實例化時,就會主動調用__enter__()方法,任務執行完成後就會調用__exit__()方法,另外,注意到,__exit__()方法是帶有三個參數的(exc_type, exc_value, traceback), 依據上面的官方說明:如果上下文運行時沒有異常發生,那麼三個參數都將置爲None, 這裏三個參數由於沒有發生異常,的確是置爲了None, 與預期一致。
  • 2 出現並拋出異常
class Test:
    def __enter__(self):
        print('__enter__() is call!')
        return self
 
    def dosomething(self):
        x = 1/0
        print('dosomethong!')
 
    def __exit__(self, exc_type, exc_value, traceback):
        print('__exit__() is call!')
        print(f'type:{exc_type}')
        print(f'value:{exc_value}')
        print(f'trace:{traceback}')
        print('__exit()__ is call!')
        # return True
 
 
with Test() as sample:
    sample.dosomething()
>>>
__enter__() is call!
Traceback (most recent call last):
__exit__() is call!
type:<class 'ZeroDivisionError'>
  File "C:/Users/xxx/PycharmProjects/Test1/test.py", line 23, in <module>
value:division by zero
    sample.dosomething()
trace:<traceback object at 0x000001C08CF32F88>
  File "C:/Users/xxx/PycharmProjects/Test1/test.py", line 10, in dosomething
__exit()__ is call!
    x = 1/0
ZeroDivisionError: division by zero

從結果可以看出, 在執行到dosomethong時就發生了異常,然後將異常傳給了__exit__(), 依據上面的官方說明:如果有異常發生,並且該方法希望抑制異常(即阻止它被傳播),則它應該返回True。否則,異常將在退出該方法時正常處理。當前__exit__並沒有寫明返回True,故會拋出異常,也是合理的,但是正常來講,程序應該是不希望它拋出異常的,這也是調用者的職責,我們將再次修改__exit__, 將其返回設置爲True。
  • 3 出現異常,阻止異常拋出
class Test:
    def __enter__(self):
        print('__enter__() is call!')
        return self
 
    def dosomething(self):
        x = 1/0
        print('dosomethong!')
 
    def __exit__(self, exc_type, exc_value, traceback):
        print('__exit__() is call!')
        print(f'type:{exc_type}')
        print(f'value:{exc_value}')
        print(f'trace:{traceback}')
        print('__exit()__ is call!')
        return True
 
 
with Test() as sample:
    sample.dosomething()
 
>>>
__enter__() is call!
__exit__() is call!
type:<class 'ZeroDivisionError'>
value:division by zero
trace:<traceback object at 0x000001C94E592F88>
__exit()__ is call!

從結果看,異常拋出被抑制了,符合預期。

參考:https://www.cnblogs.com/gengyufei/p/11341853.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章