Python-4 異常、除錯、測試、IO
阿新 • • 發佈:2020-12-05
異常
- 丟擲、捕獲異常
try... except... finally...
:
try:
x = 1
raise TypeError('型別錯誤!')
except TypeError as e: # 使用as的方式,錯誤型別會包括子類錯誤
print('型別錯誤:', e.value)
else: # 成功執行,沒有except的else,可選
print('成功運行了')
finally:
print('finally...')
print('END')
- 一般儘量使用 Python 內建的錯誤型別。
- 在
except
裡,可以使用不帶引數的raise
logging
模組
在except
中,使用logging.exception(e)
可以記錄下錯誤,但還可以使程式繼續執行。
- 也可以將錯誤資訊輸出到檔案中。
logging.basicConfig(level=logging.INFO)
logging.info('n = %d' % n)
print(10 / n)
- 還有:
debug
,info
,warning
,error
。
with
關鍵詞
with
是一種簡化的try... except... finally...
。
with open('x.txt') as fp: print fp.read()
- 注意:
with
後面處理的物件必須有__enter__()
和__exit__()
這兩個方法。 with
在執行前先執行__enter__()
,然後在finally
執行__exit__()
。- 確保了執行
finally
,做好一般的善後和異常處理,一般在資原始檔裡使用較多。
- 確保了執行
除錯
- 使用斷言
# n應該不為0
assert n != 0, 'n is zero!'
使用斷言,如果不符合條件(n
為0),則會列印後面的資訊並丟擲 AssertionError
。
python -O err.py
:不執行assert
。相當於pass
。
- 偵錯程式 pdb,單步執行
- 設定斷點:
pdb.set_trace()
單元測試
編寫一些測試條件,測試所有可能出現的代表情況,通過測試說明程式碼基本可行。
mydict.py
程式碼如下:
class Dict(dict):
def __init__(self, **kw):
super().__init__(**kw)
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError(r"'Dict' object has no attribute '%s'" % key)
def __setattr__(self, key, value):
self[key] = value
- 單元測試
import unittest
from mydict import Dict
class TestDict(unittest.TestCase):
def test_init(self):
d = Dict(a=1, b='test')
self.assertEqual(d.a, 1)
self.assertEqual(d.b, 'test')
self.assertTrue(isinstance(d, dict))
def test_key(self):
d = Dict()
d['key'] = 'value'
self.assertEqual(d.key, 'value')
def test_attr(self):
d = Dict()
d.key = 'value'
self.assertTrue('key' in d)
self.assertEqual(d['key'], 'value')
def test_keyerror(self):
d = Dict()
with self.assertRaises(KeyError):
value = d['empty']
def test_attrerror(self):
d = Dict()
with self.assertRaises(AttributeError):
value = d.empty
if __name__ == '__main__': # 設定這個語句,即當成普通的py指令碼執行測試
unittest.main()
- 繼承自
unittest.TestCase
。 - 以
test
開頭的函式才會被視為測試方法執行。 - 最常用
assertEqual()
判斷結果是否符合預期。 - 測試能否丟擲正確的異常:
# 訪問d['empty']丟擲錯誤
with self.assertRaises(KeyError):
value = d['empty']
- 另一種執行測試檔案的方法:
python -m unittest mydict_test
setUp
與tearDown
:這兩個特殊的函式會在執行每個測試方法前後被呼叫,可以用來連線資料庫等。
文件測試
在類的文件中,Python 的“文件測試”(doctest)模組可以直接提取註釋中的程式碼並執行測試。
比如重寫上述的模組:
class Dict(dict):
'''
Simple dict but also support access as x.y style.
>>> d1 = Dict()
>>> d1['x'] = 100
>>> d1.x
100
>>> d1.y = 200
>>> d1['y']
200
>>> d2 = Dict(a=1, b=2, c='3')
>>> d2.c
'3'
>>> d2['empty']
Traceback (most recent call last):
...
KeyError: 'empty'
>>> d2.empty
Traceback (most recent call last):
...
AttributeError: 'Dict' object has no attribute 'empty'
'''
# ...
那麼在呼叫該檔案時,如果沒有顯示錯誤,說明文件測試通過。
- 文件測試只會在命令列直接執行的時候才會被呼叫,平時匯入模組裡不會被呼叫。
IO 檔案操作
- 外發資料 Ouput,外部發送資料來 Input。
- 同步 IO:會堵塞等待;非同步 IO:繼續執行,跳過等待結果,有回撥模式,輪詢模式(輪詢是主執行緒主動多次去查詢結果,回撥是 IO 完子執行緒會執行的)。
在磁碟上讀寫檔案的功能都是由作業系統提供的,現代作業系統不允許普通的程式直接操作磁碟,所以,讀寫檔案就是請求作業系統開啟一個檔案物件(通常稱為檔案描述符),然後,通過作業系統提供的介面從這個檔案物件中讀取資料(讀檔案),或者把資料寫入這個檔案物件(寫檔案)。
- 讀完檔案都需要關閉檔案,避免佔用系統資源,需要
finally
/with
。
file object = open(file_name [, access_mode][, buffering])
file_name
:目標檔案,可以包括檔案的路徑。access_mode
:開啟檔案的模式,預設只讀。buffering
:緩衝。- 0 關閉(僅二進位制模式)。1 開啟行緩衝(僅文字模式)。大於 1 的表示設定緩衝區大小。負數表示使用系統預設緩衝區大小。
- 相當於字元流,位元組流。
有關模式:
Character | Meaning |
---|---|
'r' |
只讀模式 (預設 rt )。 |
'w' |
寫入模式,檔案存在則清空重新寫,不存在會自動建立。 |
'x' |
獨佔寫模式,新建一個檔案,如果該檔案已存在則會報錯。 |
'a' |
追加寫入模式,檔案存在追加寫入,不存在會自動建立。 |
'b' |
二進位制模式。 |
't' |
文字模式(預設 rt )。 |
'+' |
開啟一個檔案進行更新(可讀可寫)。 |
os 模組
os.name
:返回當前系統的名稱。posix
:Linux
、Unix
或Mac OS X
。nt
:Windows
。
os.rename(current_file_name, new_file_name)
:重新命名檔案。os.remove(file_name)
:刪除檔案。os.mkdir("newdir")
:建立目錄。os.chdir("newdir")
:改變當前目錄。os.getcwd()
:返回當前所在目錄。os.rmdir('dirname')
:刪除目錄,該目錄下的檔案需要先刪乾淨。
- 操作一個路徑的字串:
os.path.join()
:拼接路徑,可以自動識別路徑分隔符。os.path.split()
:拆分出路徑裡末尾的檔案/目錄,返回兩個元素的列表。可以自動識別路徑分隔符。os.path.splitext()
:拆分出路徑裡檔案的副檔名。
shutil
模組提供了copyfile()
函式,相當於os
模組的補充。
# 當前目錄下的所有目錄
L = [x for x in os.listdir('.') if os.path.isdir(x)]
# 當前目錄下的所有.py檔案
L2 = [x for x in os.listdir('.') if os.path.isfile(x) and os.path.splitext(x)[1] == '.py']
序列化
將儲存在記憶體中的變數等變成可儲存或可傳輸的樣式。
在 Python 中叫 pickling,在其他語言中也被稱之為 serialization,marshalling,flattening 等。反之叫做反序列化,unpickling。
pickle.dumps()
:將任意物件序列化成二進位制bytes
,可以將二進位制寫到檔案中。pickle.dump()
:將物件序列化寫入到一個file-like Object
(有read()
方法)中。
import pickle
d = dict(name='Bob', age=20, score=88)
dbytes = pickle.dumps(d) # 是一個二進位制資料
f = open('dump.txt', 'wb')
pickle.dump(d, f) # 將物件d轉儲到f中。
f.close()
dump 英 [dʌmp] 美 [dʌmp]
v. 傾倒;丟下;與(某人)結束戀愛關係;卸出(資料);(記憶體資訊)轉儲
n. 垃圾場;廢渣堆;軍需品臨時存放處;轉儲
理解:將物件從記憶體中倒出來,變成可儲存的檔案。
pickle.loads()
:將二進位制bytes
反序列化為物件。可以先將檔案讀取到二進位制,然後使用該函式。pickle.load()
:將file-like Object
直接反序列化出物件。
f = open('dump.txt', 'rb')
d = pickle.load(f) # 從f中反序列化,產生一個相同內容的物件!
f.close()
print(d)
# {'age': 20, 'score': 88, 'name': 'Bob'}
JSON
json.dump()
:直接把 JSON 寫入一個file-like Object
。json.dumps()
:將物件轉成 JSON 的字串:
import json
d = dict(name='Bob', age=20, score=88)
print(json.dumps(d)) # 返回字串
# {"age": 20, "score": 88, "name": "Bob"}
json.loads()
:將 JSON 的字串反序列化。json.loads(json_str)
json.load()
:從file-like Object
中讀取字串並反序列化。
- 總結:有
s
表示與與檔案無關,無s
的表示直接與檔案流相關。
序列化一個類
在類中定義方法返回字典,使用 json.dumps(s, default=student2dict))
即可將類例項 s
轉為 JSON 字串。
# 類轉為字典
def student2dict(std):
return {
'name': std.name,
'age': std.age,
'score': std.score
}
# 字典轉為類
def dict2student(d):
return Student(d['name'], d['age'], d['score'])
通常 class
的例項都有一個 __dict__
屬性,它就是一個 dict
,用來儲存例項變數。也有少數例外,比如定義了 __slots__
的class。