[Python]_[初級]_[關於with語句的使用方法]
阿新 • • 發佈:2018-12-01
場景
1.在很多專案程式碼裡, 經常會遇到語句with
的使用, 這個with
到底如何使用的呢, 它又會用在哪方面呢?
2.with
語句是python
裡特殊的語句, 在Java,Object-C,C++
裡是沒有的, 它是否是關鍵的語句, 沒有這個語句是不是很多場景就沒有替代方案了?
說明
0.with
語句並不是必須的語句, 它的替代方案就是try/finally
語句. 只不過with
語句需要的物件比較特殊, 需要自帶__enter
和__exit__
實現,和C++的構造和解構函式很像, 在物件生命週期開始和結束時自動分別呼叫構造和解構函式. 它只是一個具備執行流程順序的封裝語句而已, 只是在資源建立銷燬時使用, 起到精簡程式碼的作用.
1.語法
with_stmt ::= "with" with_item ("," with_item)* ":" suite
with_item ::= expression ["as" target]
或者
with EXPR as VAR:
BLOCK
python解析器在內部大概會解析為:
mgr = (EXPR) exit = type(mgr).__exit__ # Not calling it yet value = type(mgr).__enter__(mgr) exc = True try: try: VAR = value # Only if "as VAR" is present BLOCK except: # The exceptional case is handled here exc = False if not exit(mgr, *sys.exc_info()): raise # The exception is swallowed if exit() returns true finally: # The normal and non-local-goto cases are handled here if exc: exit(mgr, None, None, None)
with
內部執行順序說明:
- 1.在
with_item
裡的上下文表達式執行獲取一個context manager
. - 2.載入
context manager
力的__exit__()
, 注意是載入, 不是呼叫. - 3.呼叫
context manager
裡的__enter__
函式. - 4.如果
target
或者說有as
關鍵字, 那麼__enter__
方法的返回值賦值給target
. - 5.執行
suite
- 6.
context manager
的__exit__
被呼叫, 如果有異常, 那麼traceback
會傳遞給__exit__
, 否則傳遞None
2.with
try/finally
使用方式. 注意, 沒有except
, 該丟擲的異常還是會丟擲的. 它的重點在context manager
上, 滿足contextmanager protocol
(協議)的物件稱之為context manager
. 這個協議物件必須實現了 __enter__()
和 __exit__()
方法.在標準庫裡, files
, sockets
, 和locks
物件都實現了這個協議.
比如
- file
- thread.LockType
- threading.Lock
- threading.RLock
- threading.Condition
- threading.Semaphore
- threading.BoundedSemaphore
3.open()
的位置在 The Python Standard Library
->Built in Functions
裡. 如果開啟失敗, 那麼會丟擲一個 OSError
錯誤. with
是捕抓不到這個異常的.
4.自定義contextmanager
的方式有兩種, 一種是定義一個類, 並實現__enter__
和__exit__
方法; 另外一種是使用@contextmanager
來修飾generator
函式, 這種比較方便, 只需要定義一個函式即可.
例子
以下例子列舉了contextmanager
的兩種建立方式:
import io
from contextlib import contextmanager
class tranaction:
def __init__(self, obj):
print ("get a db handle")
self.obj = obj
def __enter__(self):
print ("begin db tranaction")
return self.obj
def __exit__(self, *exc_info):
if exc_info is None:
print ("rollback db tranaction")
else:
print ("commit db tranaction")
@contextmanager
def TranactionDecorator(obj):
print("begin db tranaction")
try:
yield None
except Exception:
print("do db rollback")
else:
print("commit db tranaction")
if __name__ == '__main__':
print ("====== tranaction class ======")
with tranaction(None):
print ("do insert intot sql")
print("====== Tranaction Generator1 ======")
with TranactionDecorator(None):
print("do insert intot sql-1")
raise 1
print("====== Tranaction Generator2 ======")
with TranactionDecorator(None):
print("do insert intot sql-2")
輸出
====== tranaction class ======
get a db handle
begin db tranaction
do insert intot sql
commit db tranaction
====== Tranaction Generator1 ======
begin db tranaction
do insert intot sql-1
do db rollback
====== Tranaction Generator2 ======
begin db tranaction
do insert intot sql-2
commit db tranaction
參考
-
Python document 裡的 8.5. The with statement.
file object
An object exposing a file-oriented API (with methods such as read() or write()) to an underlying
resource. Depending on the way it was created, a file object can mediate access to a real
on-disk file or to another type of storage or communication device (for example standard
input/output, in-memory buffers, sockets, pipes, etc.). File objects are also called file-like
objects or streams.
There are actually three categories of file objects: raw binary files, buffered binary files
and text files. Their interfaces are defined in the io module. The canonical way to
create a file object is by using the open() function
generator
A function which returns a generator iterator. It looks like a normal function except that it contains yield expressions for producing a series of values usable in a for-loop or that can be retrieved one at a time with the next() function.
Usually refers to a generator function, but may refer to a generator iterator in some contexts. In cases where the intended meaning isn’t clear, using the full terms avoids ambiguity.
generator iterator
An object created by a generator function.
Each yield temporarily suspends processing, remembering the location execution state (including local variables and pending try-statements). When the generator iterator resumes, it picks up where it left off (in contrast to functions which start fresh on every invocation).
常見的contextmanager
修飾符實現方式:
class GeneratorContextManager(object):
def __init__(self, gen):
self.gen = gen
def __enter__(self):
try:
return self.gen.next()
except StopIteration:
raise RuntimeError("generator didn't yield")
def __exit__(self, type, value, traceback):
if type is None:
try:
self.gen.next()
except StopIteration:
return
else:
raise RuntimeError("generator didn't stop")
else:
try:
self.gen.throw(type, value, traceback)
raise RuntimeError("generator didn't stop after throw()")
except StopIteration:
return True
except:
# only re-raise if it's *not* the exception that was
# passed to throw(), because __exit__() must not raise
# an exception unless __exit__() itself failed. But
# throw() has to raise the exception to signal
# propagation, so this fixes the impedance mismatch
# between the throw() protocol and the __exit__()
# protocol.
#
if sys.exc_info()[1] is not value:
raise
def contextmanager(func):
def helper(*args, **kwds):
return GeneratorContextManager(func(*args, **kwds))
return helper