1. 程式人生 > >With語句

With語句

With語句

With介紹:
with是從Python2.5引入的一個新的語法,它是一種上下文管理協議,目的在於從流程圖中把 try,except 和finally 關鍵字和資源分配釋放相關代碼統統去掉,簡化try….except….finlally的處理流程。with通過enter方法初始化,然後在exit中做善後以及處理異常。所以使用with處理的對象必須有enter()和exit()這兩個方法。其中
enter()方法在語句體(with語句包裹起來的代碼塊)執行之前進入運行,exit()方法在語句體執行完畢退出後運行。with 語句適用於對資源進行訪問的場合,確保不管使用過程中是否發生異常都會執行必要的“清理”操作,釋放資源,比如文件使用後自動關閉、線程中鎖的自動獲取和釋放等。

With基本語法
With語句的基本語法格式如下:
with expression [as target]:
with_body
參數說明:
expression:是一個需要執行的表達式;
target:是一個變量或者元組,存儲的是expression表達式執行返回的結果,
可選參數

With 語句是什麽?
有一些任務,可能事先需要設置,事後做清理工作。對於這種場景,Python的with語句提供了一種非常方便的處理方式。一個很好的例子是文件處理,你需要獲取一個文件句柄,從文件中讀取數據,然後關閉文件句柄。
如果不用with語句,代碼如下:
file = open("/tmp/foo.txt")

data = file.read()file.close()
file.close()
這裏有兩個問題:
一是可能忘記關閉文件句柄;?
二是文件讀取數據發生異常,沒有進行任何處理。
下面是處理異常的加強版本:

try:
f = open(‘xxx‘)
except:
    print ‘fail to open‘
exit(-1)
try:
do something
except:
do something
finally:
     f.close()

雖然這段代碼運行良好,但是太冗長了。
這時候就是with一展身手的時候了。除了有更優雅的語法,with還可以很好的處理上下文環境產生的異常。
下面是with版本的代碼:

with open("/tmp/foo.txt") as file:
    data = file.read()

with的工作原理:
緊跟with後面的語句被求值後,返回對象的enter()方法被調用,這個方
法的返回值將被賦值給as關鍵字後面的變量;
當with後面的代碼塊全部被執行完之後,將調用前面返回對象的exit()方法。

代碼示例:

class Sample(object):
    def __enter__(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

執行結果:
技術分享圖片

E:\python>python ten.py
In enter()
sample: Foo
In exit()

以上代碼執行過程
1.enter()方法被執行
2.enter()方法返回的值 - 這個例子中是”Foo”,賦值給變量’sample’
3.執行代碼塊,打印變量”sample”的值為 “Foo”
4.exit()方法被調用?
with真正強大之處是它可以處理異常。可能你已經註意到Sample類的 exit 方法有三個參數 val, type 和 trace。 這些參數在異常處理中相當有用。
c```
lass Sample:
def enter(self):
return self

def __exit__(self, type, value, trace):
    print "type:", type
    print "value:", value
    print "trace:", trace

def do_something(self):
    bar = 1/0
    return bar + 10

with Sample() as sample:
sample.do_something()

Sample()的 enter() 方法返回新創建的Sample對象,並賦值給變量sample。

執行結果:
type: <type ‘exceptions.ZeroDivisionError‘>
value: integer division or modulo by zero
trace: <traceback object at 0x00000000026A5B08>
Traceback (most recent call last):
File "ten.py", line 16, in <module>
sample.do_something()
File "ten.py", line 12, in do_something
bar = 1/0
ZeroDivisionError: integer division or modulo by zero
實際上,在with後面的代碼塊拋出任何異常時,exit() 方法被執行。正如例子所示,異常拋出時,與之關聯的type,value和stack trace傳給 exit() 方法,因此拋出的ZeroDivisionError異常被打印出來了。開發庫時,清理資源,關閉文件等等操作,都可以放在 exit 方法當中。
上文說了 exit 函數可以進行部分異常的處理,如果我們不在這個函數中處理異常,他會正常拋出,這時候我們可以這樣寫(python 2.7及以上版本,之前的版本參考使用contextlib.nested這個庫函數):

try:
with open("e:\python\222.txt") as f:
do someting
else:
do something about exception

總之,with-as表達式極大的簡化了每次寫finally的工作,這對保持代碼的優雅性是有極大幫助的。

如果有多個項,可以這麽寫:

with open("e:\python\222.txt") as f1,open("e:\python\hu.txt") as f2:
content1 = f1.read()
content2 = f2.read()
print content1,content2


**自定義with異常**
從前面我們知道,with語句最關鍵的地方在於被求值對象必須有
__enter__()和__exit__()這兩個方法,那我們就可以通過自己實現這兩
示例代碼:
方法來自定義with語句處理異常。

#```
coding=utf-8
class opened(object):
    def __init__(self, filename):
         self.handle = open(filename)
         print ‘Resource: %s‘ %filename

    def __enter__(self):
        print ‘[Enter %s]: Allocate resource.‘ % self.handle
        return self.handle #可以返回不同的對象

    def __exit__(self, exc_type, exc_value, exc_trackback):
            print ‘[Exit %s]: Free resource.‘ % self.handle
            if exc_trackback is None:
                print ‘[Exit %s]: Exited without exception.‘ % self.handle
            else:
               print ‘[Exit %s]: Exited with exception raised.‘ % self.handle
               return False # 可以省略,缺省的None也是被看做是False
            self.handle.close()

with opened(r‘e:\\python\\2.txt‘) as fp:
    for line in fp.readlines():
        print(line)

執行結果:

E:\python>python ten.py
Resource: e:\python\222.txt
[Enter <open file ‘e:\\python\\222.txt‘, mode ‘r‘ at 0x00000000026B8540>]: Allocate resource.
hha222
[Exit <open file ‘e:\\python\\222.txt‘, mode ‘r‘ at 0x00000000026B8540>]: Free resource.
[Exit <open file ‘e:\\python\\222.txt‘, mode ‘r‘ at 0x00000000026B8540>]: Exited without exception.

以上示例代碼說明:
opened中的enter() 返回的是自身的引用,這個引用可以賦值給as 子句中的fp變量;返回值的類型可以根據實際需要設置為不同的類型,不必是上下文管理器對象本身。
exit() 方法中對變量exc_trackback進行檢測,如果不為 None,表示發生了異常,返回 False 表示需要由外部代碼邏輯對異常進行處理;如果沒有發生異常,缺省的返回值為 None,在布爾環境中也是被看做False,但是由於沒有異常發生,exit() 的三個參數都為 None,上下文管理代碼可以檢測這種情況,做正常處理。exit()方法的3個參數,分別代表異常的類型、值、以及堆棧信息

With語句