前言:
在對一些資源進行訪問時,常常會出現操作不當或出現異常而導致資源沒有得到必要關閉資源釋放資源。例如:文件讀取、socket等等。
下面內容以文件讀取open方法爲例。
原始操作:
f=open("filename")
f.write()#文件操作
f.close()
上述代碼存在的問題:
(1)容易忘記文件關閉。
(2)當文件操作出現異常導致程序提早離開,而沒有執行關閉文件操作。
優化版:
try:
f=open("xxx")
f.write() #文件操作
except:
do something
finally:
f.close()
上述代碼:
雖然解決因爲出現異常而導致沒有關閉文件的問題。但是這樣使得代碼冗餘度加大,最最重要的是這樣一點都不pythonic。
with的用法:
with open("xxx") as f:
f.write() #文件操作
問題來了,
with爲什麼可以做到自動關閉文件?
with爲什麼即便文件操作中出現異常也可以正常關閉文件?
with語句和原始open同樣返回一對象,有什麼不一樣?
with原理:
基本思想是with所求值的對象必須有一個__enter__()方法,一個__exit__()方法。
緊跟with後面的語句被求值後,返回對象的__enter__()方法被調用,這個方法的返回值將被賦值給as後面的變量。當with後面的代碼塊全部被執行完之後,將調用前面返回對象的__exit__()方法。
我們具體來看看with爲什麼可以做到自動關閉資源。
1 class Test(object):
2 def __enter__(self):
3 print("執行了 __enter__方法")
4 return "enter返回的內容"
5
6 def __exit__(self, type, value, trace):
7 print("執行了 __exit__方法")
8
9
10 with Test() as test:
11 print("test:", test)
運行結果:
執行了 __enter__方法
test: enter返回的內容
執行了 __exit__方法
執行過程分析:
with後面的語句執行、enter()執行
enter()返回值返回給as後面的變量test
執行with語句中間代碼塊打印變量test、
中間代碼塊執行完後執行__exit__()
推斷:自動關閉文件是在__exit__()中調用文件關閉方法。
實現支持with的類
接下我們改進一下代碼來看看with爲什麼可以處理異常出現的情況
class Test(object):
def __enter__(self):
print("執行了 __enter__方法")
return self
def __exit__(self, type, value, trace):
print("執行了 __exit__方法")
print("type:", type)
print("value:", value)
print("trace:", trace)
def do_something(self):
bar = 1 / 0
return bar + 10
with Test() as test:
test.do_something()
運行結果:
執行了 __enter__方法
執行了 __exit__方法
Traceback (most recent call last):
File "E:/Code/PycharmProjects/untitled7/t2.py", line 19, in <module>
type: <class 'ZeroDivisionError'>
test.do_something()
value: division by zero
trace: <traceback object at 0x030E1828>
File "E:/Code/PycharmProjects/untitled7/t2.py", line 14, in do_something
bar = 1 / 0
ZeroDivisionError: division by zero
進程完成,退出碼 1
先給分析一下代碼,Test()的__enter__()方法返回新創建的Test對象,並賦值給變量test。然後執行會出現異常的方法,exit()中打印其三個參數。
根據運行結果,很明顯__exit__()得三個參數分別是異常類、異常值、異常信息追蹤。
實際上,當with中間代碼體出現異常時__enter__()就會執行,並把異常相關信息賦值給三參數。同時在這個方法中我們還可以加入清理資源,關閉文件等等操作。
總的看來,python的with語句是一個十分巧妙有效的機制,它可以讓代碼更加的簡潔、更加的pythonic。