Python with/as 環境管理器
with語句是從Python 2.5開始引入的一種與異常處理相關的功能(2.5版本中要通過 from __future__ import with_statement
匯入後才可以使用,
從2.6 版本開始預設可用。
with/as語句的設計是用於作為常見的try/finally用法模式的替代方案,確保執行一些必要的終止或者清理工作。python以環境管理器強化一些內建工具,
例如自動關閉檔案,執行緒中的自動上鎖和解鎖等,程式設計師也可以用類來編寫自己的環境管理器(__enter__
, __exit__
)。
with語句的基本用法
with expression [as variable]:
"""
with_block
put your code here
"""
... code ...
expression要返回一個物件,從而支援環境管理協議(下面會談到這個協議)。
as語句是選用的,expression語句會返回一個值,這個值會賦值給as後面的variable變數。(注意不是把這個物件賦值給variable變數,是把這個物件返回
一個值賦值給variable,這個後面會談到)
python的一些內建物件已經支援了環境管理協議,如檔案物件。使用with語句可以在with_block之後自動關閉檔案,無論是否發生異常。
for example:
with open("test_file.txt", "r") as myfile:
for line in myfile:
print(line)
這裡開啟檔案物件後,返回了被開啟檔案物件的例項並賦給了myfile變數。對myfile處理完成後,with語句會自動執行myfile.close()。
相當於try/finally語句:
myfile = open("test_file.txt", "r")
try:
for line in myfile:
print(line)
finally:
myfile.close()
環境管理協議
python有一些內建物件已經帶有了環境管理器,如剛剛談過的檔案物件。但我們仍然可以自己編寫一個帶有環境管理器的類。
這個實際上就是類的運算子過載,需要使用__enter__
__exit__
。 先簡述一些with語句的實際工作流程:
1. 計算expression表示式,所得到的物件就稱為環境管理器,它必須有
__enter__
和__exit__
方法。 2. 環境管理器的
__enter__
會首先被呼叫,如果存在as語句,它的返回值會賦給as後面的變數,否則直接丟棄。 3. with_block中的程式碼塊會執行。
4. 最後執行
__exit__(type, value, traceback)
方法。 - 如果with語句引發了異常,且該方法的返回值是假,那麼異常就會被重新引發,否則異常會終止。正常情況下我們希望異常被重新引發,這樣才能講異常 傳遞到with程式碼塊外面去,便於發現和處理。
- 如果with語句沒有引發異常,其中type, value, traceback引數都會以None值傳遞。
現在我們來自己定義一個環境管理器物件:
class Person:
def __init__(self, name):
self.name = name
def call(self, number):
print("{person} is calling {number}".format(person=self.name, number=number))
def __enter__(self):
print("{person} pick up the phone".format(person=self.name))
print("ready to call")
return self
def __exit__(self, exit_type, exit_value, exit_traceback):
try:
if exit_type is None:
print("ok, thers is no exception. ")
else:
print("there are some exceptions")
print(exit_type, exit_value, exit_traceback)
return False
finally:
print("{person} cut the call".format(person=self.name))
# return True
# test1
with Person("Jon") as jon:
jon.call("110")
# test 2
with Person("Jon") as jon:
jon.call("120")
raise ValueError("the phone number is wrong")
輸出:
# test1
Jon pick up the phone
ready to call
Jon is calling 110
ok, thers is no exception.
Jon cut the call
# test 2
Jon pick up the phone
ready to call
Jon is calling 120
there are some exceptions
(<type 'exceptions.ValueError'>, ValueError('the phone number is wrong',), <traceback object at 0x7fa717085a28>)
Jon cut the call
Traceback (most recent call last):
File "/home/vincent/github/practice/test.py", line 35, in <module>
raise ValueError("the phone number is wrong")
ValueError: the phone number is wrong
關於異常是否丟擲:
前面已經說過,在__exit__
中如果返回假(包括False,None)等,那麼with語句中出現的異常就會被丟擲來。如上面test2的輸出一樣。
但如果return True
,如我上面注掉的一樣,那麼with語句中出現的異常就會被自動過濾掉,不會丟擲來,那麼test2的輸出就該是:
Jon pick up the phone
ready to call
Jon is calling 120
there are some exceptions
(<type 'exceptions.ValueError'>, ValueError('the phone number is wrong',), <traceback object at 0x7fa717085a28>)
Jon cut the call
發現沒有,雖然with語句捕獲到了異常,我們在程式碼裡手動把它print了出來。但實際上並不會丟擲異常,也就是說程式碼會忽略這個異常繼續執行下去。
建議將異常丟擲,除非你有十足的理由。