Categories
程式開發

Python中的with是測試常用到的資源打開利器


在我們完成自動化測試代碼的時候,總會遇見各種讀取文本文件、讀取Excel等類型的操作。 這種代碼我們時刻都要記得打開文件後要關閉文件。 往往關閉文件卻是我們常常忘記的。 針對上述這樣的情況,python提供了with就可以完美解決這個問題,這也是python的語法糖。 Syntactic sugar,也就是語法糖,它指計算機語言中添加的某種語法,這種語法對語言的功能沒有影響,但是更方便程序員使用。 語法糖讓程序更加簡潔,有更高的可讀性。 語法糖就是為了避免coder出現錯誤並提高效率的語法層面的一種優雅的解決方案。

一個常規的文件打開代碼

下面是一個常規的打開文件的代碼,那麼你可以從下面代碼中看出什麼問題嗎?

rf= open('crisschan.txt','r')

print(rf.readlines())

rf.close()

上面代碼在讀取文件過程中如果發生異常,那麼close()函數就沒有辦法被執行到了,這也就導致了文件沒有辦法關閉了。 因此,很多教程上都告訴大家要用try except將文件讀取的異常捕獲到,那麼我們改造一下如下:

try:

rf = open('email.txt','r')

print(rf.readlines())

except:

print('Oooooops!What's up!')

finally:

rf.close()

好了,上面的代碼無論如何我們都會執行close函數了,這樣是不是已經很好了。 但是上面的代碼太繁瑣了,這樣的coding段,python提供了with,讓上述的代碼更簡單:

with open('email.txt','r') as rf:

print(rf.readlines())

上面是不是很簡潔,是不是也很優雅呢。

with是怎麼幹活的

with context_expression [as target(s)]:

do_somthing()

上述代碼片段中,`context_expression`會返回一個上下文管理器對象,這個對象並不賦值給`as`後的`target(s)`,而是上下文管理器的`__enter__()`函數的返回值賦值給`target(s)`。 當with全部的代碼段都執行完成後,會調用`__exit__()`。

具體例子如下:

class SampleWith(object):

def __init__(self):

print('init this class')

def __enter__(self):

print('this is __enter__')

return 'CrissChan'

def __exit__(self, exc_type, exc_val, exc_tb):

print('this is __exit__')

def call(self):

print('call funtion')

if __name__ == '__main__':

with SampleWith() as sw:

print('sw is :',sw)

sw.call()

運行完後,輸入如下:

init this class

this is __enter__

sw is : CrissChan

this is __exit__

Traceback (most recent call last):

File "/Users/crisschan/PycharmProjects/try_space/flv2mp4.py", line 24, in

sw.call()

AttributeError: 'str' object has no attribute 'call'

那下面我來給你解釋一下上面的代碼段以及結果輸出。

1、在main函數中我們使用with調用了SampleWith(),這是時候我們就會看到了我們險實力話了一個SampleWith類,調用了他的`__init__(self)`構造函數,2、接下來因為我們使用了with這個語法糖,因此下面調用了`__enter__(self)`3、在後面我使用了`as sw`,也就是我將`__enter__(self)`的return賦值給了`sw`,那麼也就是說`sw`存儲的是字符串`CrissChan`,那麼也就打印了`sw is : CrissChan`4、後面調用了sw.call()就出了問題,因為字符串沒有call方法。 但是仍舊進入了`__exit__(self, exc_type, exc_val, exc_tb)`函數。

下面我將上面有報錯的代碼修改一下,如下:

class SampleWith(object):

def __init__(self):

print('init this class')

def __enter__(self):

print('this is __enter__')

return 'CrissChan'

def __exit__(self, exc_type, exc_val, exc_tb):

print('this is __exit__')

def call(self):

print('call funtion')

if __name__ == '__main__':

with SampleWith() as sw:

print('sw is :',sw)

上面代碼就不會報錯了。 從上面可以看出就算中間除了異常,放在`__exit__()`中的邏輯段還是會被執行的。 想要了解更多請看官方的文檔[pep-403](https://www.python.org/dev/peps/pep-0343/)

特別備註:

exit()方法中有3個參數, exc_type, exc_val, exc_tb,這些參數在異常處理中相當有用。

exc_type: 錯誤的類型

exc_val: 錯誤類型對應的值

exc_tb: 代碼中錯誤發生的位置