python 基礎知識整理學習
錯誤處理、調試與測試
提前捕獲並處理代碼中可能出現的錯誤,使代碼更健壯。
錯誤處理
代碼中報錯會中斷程序,不會繼續執行。
# 打印輸出未定義的變量,報錯後不會輸出後面的數字
print(a)
print(2)
輸出錯誤:
try…except…finally
錯誤捕獲,記錄錯誤,方便查詢
try:
print(a)
except NameError as e:
print("變量未聲明,請檢查")
finally:
print("繼續執行!")
print(2)
說明:
- 剛纔的錯誤類型爲
NameError
,使用except
語句捕獲並處理; - 所有的錯誤類型都繼承
BaseException
基類,錯誤捕獲得從子類到父類,層層向上捕獲。傳遞門 - 錯誤會被層層傳遞,主要在於函數的調用,在哪調用錯誤會先從哪拋出,一直拋給系統,程序終止。
logging記錄
通過logging
可以把錯誤記錄到錯誤日誌中,方便日後查看。也可以打印出來,程序不會被終止。
# logging 記錄日誌
import logging
try:
print(10/0)
except ZeroDivisionError as e:
logging.exception(e)
print(0/10)
說明:
logging
類型記錄包括:debug
/error
/info
/warning
- 默認的
logging.info()
是不會輸出的。通過配置logging.basicConfig(level=logging.INFO)
自定義錯誤
系統內置的錯誤類型不能滿足工作,還可以自定義錯誤類型,並拋出。
# 自定義錯誤類型
class ConflictValueError(ValueError):
pass
name = input("please input your name \n")
try:
if name == "admin":
raise ConflictValueError("you can't use this name")
except ConflictValueError as e:
logging.exception(e)
print("ok")
說明:
- 儘可能使用系統內置的錯誤類型,自定義類型繼承系統的某一個類型錯誤
BaseException
。 - 錯誤捕獲處理一定是有助於問題排查。
調試
功能測試,問題排查。
print()
打印結果輸出,標記;- 斷言
assert
,斷言失敗會拋出AssertionError
錯誤
說明:# 斷言 assert name!="test","測試" print("大神,你好")
- 斷言拋出錯誤後,程序終止執行
- 通過執行時加入參數
python -O four.py
忽略斷言。
logging
日誌輸出記錄。- 調試器
pdb
python -m pdb **.py
啓動調試,按步執行。
說明:l
查看代碼,會顯示當前執行位置。n
執行下一步。p 變量名
來查看已執行的語句中的變量的值。q
結束退出。c
程序開始正常執行。
pdb.set_trace()
設置斷點,程序執行到這停止。
需要導入import pdb
單元測試
編寫一個簡單的用戶名校驗類,自定義各種錯誤類型,放置在單獨的文件four_form.py
:
# 自定義表單校驗錯誤類
class ConflictValueError(ValueError):
pass
class ExistError(ValueError):
pass
class PureNumberError(ValueError):
pass
class LenError(ValueError):
pass
# 表單用戶名校驗 、 不能是純數字、不能有特殊符號、必須6位以上
import re
class ValidForm():
def __init__(self,**ot):
self.form = ot
def exist_name(self):
form = self.form
if "name" in form:
pass
else:
raise ExistError("必須填寫用戶名!")
def conflict_name(self):
form = self.form
self.exist_name()
if form["name"] in ["admin","test","login"]:
raise ConflictValueError("你不能使用這個名字")
def pureNumber_name(self):
form = self.form
self.conflict_name()
if re.search(r'^\d+$',str(form["name"]))!=None:
raise PureNumberError("不能是純數字!")
def len_name(self):
form = self.form
self.pureNumber_name()
if len(form["name"]) <6:
raise LenError("用戶名不少於6個字符!")
def isAvailable(self):
self.len_name()
return True
# 測試方法
# ValidForm(**{"name":"232"}).isAvailable()
編寫測試函數,單獨的文件four_test.py
:
import unittest
from four import ValidForm,ExistError,ConflictValueError,LenError
class TestValidForm(unittest.TestCase):
# 在每個測試方法開始前調用 before
def setUp(self):
print("start")
def test_existError(self):
with self.assertRaises(ExistError):
ValidForm().exist_name()
def test_conflicError(self):
with self.assertRaises(ConflictValueError):
ValidForm(**{"name":"admin"}).conflict_name()
@unittest.skip("將不會被調用")
def test_lenError(self):
with self.assertRaises(LenError):
ValidForm(**{"name":"abc"}).len_name()
# 方法調用後執行
def tearDown(self):
print("end")
if __name__ == '__main__':
unittest.main()
說明:
- 依賴模塊
unittest
,需導入; - 不同文件中,需要調用用到的模塊
from ... import **
unittest.main()
提供了在命令行運行測試腳本的接口,直接運行python four_test.py
setUp()
每個測試方法執行前都會去調用;tearDown()
測試方法執行完後調用unittest
內部提供了斷言的方法,如assertRaises()
/assertEqual()
/assertTrue()
,- 註解可以改變方法的表現,比如
@unittest.skip()
使得這個測試方法不會被調用;
文檔測試
以註釋的方式寫測試用例,然後指定執行註釋中可執行的代碼:
import re
class ValidForm():
'''
valid name
>>> ValidForm().exist_name()
Traceback (most recent call last):
...
ExistError: 必須填寫用戶名!
>>> ValidForm(**{"name":"admin"}).conflict_name()
Traceback (most recent call last):
...
ConflictValueError: 你不能使用這個名字
>>> ValidForm(**{"name":"admin_123"}).isAvailable()
True
'''
def __init__(self,**ot):
self.form = ot
# 省略.....
if __name__ == "__main__":
import doctest
doctest.testmod()
說明:
>>> **
必須有空格,纔會當做可執行命令執行;- 期望一定要和執行的結果一致;不一致時會報錯
Expected: ... Got: ...
- 當符合期望時,執行沒有任何輸出。改錯一處
ValidForm(**{"name":"ad_min"}).conflict_name()
執行結果和期待的不一致:
doctest
只會在命令行模式下執行。>>>
標識決定了執行環境。