小結:遇到錯誤怎么辦?
1、不理它。
2、捕獲它,再拋出。
3、捕獲並處理錯誤。
raise
不懂如何處理錯誤就直接拋出(raise),交由合適的層次處理,有時候需要自定義錯誤,但是通常使用Python內置的錯誤就可以了:
#!/usr/bin/env python3 # -*- coding: utf-8 -*- # 自定義的異常 class MyError(Exception): pass # 拋出異常的函數 def f(): raise MyError('This is my error!') # 不處理,繼續拋 def f2(): f() # 不處理,繼續拋 def f3(): f2() print('hi1') # 被打印 f3() print('hi2') # 不會被打印,因為程序因為出錯而終止了
處理機的調用和函數調用的方向剛好相反,因為最終都沒有人來處理這個錯誤,所以錯誤被一直上拋,直到被Python解釋器捕獲:
hi1 Traceback (most recent call last): File "D:\labs\test.py", line 21, in <module> File "D:\labs\test.py", line 18, in f3 File "D:\labs\test.py", line 14, in f2 File "D:\labs\test.py", line 10, in f __main__.MyError: This is my error!
不懂如何處理錯誤的第二種方式是記錄一下再繼續拋出,這需要用到“try...except”:
# 自定義的異常 class MyError(Exception): pass # 拋出異常的函數 def f(): raise MyError('This is my error!') # 不處理,繼續拋 def f2(): f() # 不處理,繼續拋 def f3(): f2() print('hi1') # 被打印 try: f3() except Exception as e: print('...mark') raise # 原樣拋出 finally: print('b') print('hi2') # 錯誤沒被處理了,程序不再執行
輸出:
hi1 ...mark b Traceback (most recent call last): File "D:\labs\test.py", line 23, in <module> File "D:\labs\test.py", line 18, in f3 File "D:\labs\test.py", line 14, in f2 File "D:\labs\test.py", line 10, in f __main__.MyError: This is my error!
(可以觀察到finally是無論如何都會在最后被執行的!)
try...except
錯誤一般由底層代碼拋出,通常是“他人的代碼”,所以更經常寫的是try...except別人拋出的錯誤。
那么如何自己來處理錯誤呢?當位於合適的層次的時候我們用“try...except”來處理錯誤:
print('hi1') # 被打印 try: f3() except Exception as e: print('a:', e) finally: print('b') print('hi2') # 錯誤被處理了,程序繼續執行
輸出情況:
hi1 a: This is my error! b hi2
測試證明except 父類錯誤就可以捕獲子類錯誤。
在try的內部一但raise了錯誤,如果有except語句將其捕獲,那么try語句塊的剩余語句是不會被執行的,因此try語句塊要設定合適的范圍,而不是一次try大量的語塊:
class Error1(Exception): pass try: print('1') raise Error1('Error1') print('2') # 不被執行 print('3') # 不被執行 except Exception as e: print(e) print('continue execute')
1 Error1 continue execute
對於捕獲多個錯誤的情況:
並不是多個錯誤都會被捕獲,因為一旦raise了第一個錯誤,try語句塊的程序就不會再繼續執行了,所以同時拋出多個錯誤的情況是不存在的,書寫多個except語句只是為了逐級排查最終捕獲一個錯誤!
Python的except語句還可以加上else,如果沒有錯誤將會執行else內的語句。
logging
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import logging class Error1(Exception): pass try: print('1') raise Error1('I make a mistake') print('2') # 不被執行 print('3') # 不被執行 except Exception as e: print('4') logging.exception(e) print('5') print('continue execute')
1 4 ERROR:root:I make a mistake Traceback (most recent call last): File "test.py", line 10, in <module> raise Error1('I make a mistake') Error1: I make a mistake 5 continue execute
今天遇到了一個問題卡了好一會兒:TabError: inconsistent use of tabs and spaces in indentation。原因是混用了空格和tab(可能是copy代碼帶來的)。最后刪掉重輸了一遍就解決了。
debug的方法:
1、print
2、斷言:print的升級版。
3、logging:既可以輸出到console,也可以輸出到文件、不打斷程序執行。
在使用logging之前還需要在程序頭添加一行配置:
import logging logging.basicConfig(level=logging.WARNING)
level指定了記錄信息的級別,分為DEBUG、INFO、WARNING、ERROR。當level指定為DEBUG,記錄debug及以上的logging信息,當level指定為INFO,記錄info及以上的logging信息,以此類推:
import logging logging.basicConfig(level=logging.DEBUG) print(1) logging.debug('debug:hi') logging.info('info:hi') logging.warning('warning:hi') logging.error('error') print(2)
輸出:
1
DEBUG:root:debug:hi
INFO:root:info:hi
WARNING:root:warning:hi
ERROR:root:error
2
繼續修改level的等級:
logging.basicConfig(level=logging.INFO)
1
INFO:root:info:hi
WARNING:root:warning:hi
ERROR:root:error
2
...
logging.basicConfig(level=logging.WARNING)
1
WARNING:root:warning:hi
ERROR:root:error
2
...
logging.basicConfig(level=logging.ERROR)
1
ERROR:root:error
2
4、pdb
在命令行下開啟dubug模式(參數要加在文件名前面) :
$ python -m pdb test.py
輸入n單步執行代碼:
$ python -m pdb test.py > d:\labs\test.py(3)<module>() -> n = 0 (Pdb) n > d:\labs\test.py(4)<module>() -> print(1) (Pdb) n 1 > d:\labs\test.py(5)<module>() -> n = n + 1 (Pdb)
隨時可以用 p 變量名查看變量:
> $ test.py(9)<module>() -> n = n + 1 (Pdb) p n 2
使用q來退出。
最后,從某處開始debug的方法:
import pdb print(1) print(2) pdb.set_trace() # 從這里開始進入debug模式。 print(3) print(4) print(5)
直接運行.py程序就可以了。
ps:一直執行n命令將循環運行程序。
Q1:什么是單元測試?
Q2:如何進行單元測試?
A1:單元測試是指對一個模塊、一個函數或者一個類來進行正確性檢驗的測試。
A2:例如我要檢測自己寫的sum()函數好不好用。
def sum(x, y): return x + y
1、首先需要編寫測試用例,這需要用到Python自帶的unittest模塊:
import unittest from test import sum
class TestSum(unittest.TestCase): def test_func(self): self.assertEqual(sum(1, 2), 3) self.assertEqual(sum(1, -2), -1) self.assertEqual(sum(100, -2), 98) self.assertEqual(sum(1001, 110), 1111)
assertEqual是用於判定兩個參數是否相等的函數,unittest模塊中還有很多用於檢測正確性的函數。 凡是test_xxx在測試的時候都會被運行。
import unittest from test import sum class TestSum(unittest.TestCase): def test_func(self): self.assertEqual(sum(1, 2), 3) self.assertEqual(sum(1, -2), 0) self.assertEqual(sum(100, -2), 98) self.assertEqual(sum(1001, 110), 1111) def test_func2(self): self.assertEqual(sum(1, 2), 3) self.assertEqual(sum(1, -2), 0) self.assertEqual(sum(100, -2), 90) self.assertEqual(sum(1001, 110), 1111) def test_func3(self): self.assertEqual(sum(1, 2), 3) self.assertEqual(sum(1, -2), -1) self.assertEqual(sum(100, -2), 98) self.assertEqual(sum(1001, 110), 1111) def test_func4(self): print('hi~') print('#$@#%@#$^%%#$^@@#$^&*')
2、運行測試用例:
$ python -m unittest sum_test.py . ---------------------------------------------------------------------- Ran 1 test in 0.000s OK
$ python -m unittest sum_test.py FF.hi~ #$@#%@#$^%%#$^@@#$^&* . ====================================================================== FAIL: test_func (sum_test.TestSum) ---------------------------------------------------------------------- Traceback (most recent call last): File "D:\labs\sum_test.py", line 9, in test_func self.assertEqual(sum(1, -2), 0) AssertionError: -1 != 0 ====================================================================== FAIL: test_func2 (sum_test.TestSum) ---------------------------------------------------------------------- Traceback (most recent call last): File "D:\labs\sum_test.py", line 15, in test_func2 self.assertEqual(sum(1, -2), 0) AssertionError: -1 != 0 ---------------------------------------------------------------------- Ran 4 tests in 0.000s FAILED (failures=2)
有時候可能會期待是不是拋出一些錯誤,可以這么寫:
def test_attrerror(self): d = Dict() with self.assertRaises(AttributeError): value = d.empty # raise AttributeError()
在測試用例中還可以添加setUp和tearDown函數,這兩個函數分別在測試開始時和測試結束時被調用。
1、搭建測試環境。
def sum(x, y): ''' test my sum funtion ''' return x + y if __name__ == '__main__': import doctest doctest.testmod()
$ python test.py # 無輸出
2、編寫測試代碼
def sum(x, y): ''' test my sum funtion >>> sum(2, 3) 5 >>> sum(1, 9) 10 ''' return x + y if __name__ == '__main__': import doctest doctest.testmod()
如果代碼測試無誤就不會有輸出。
如果有錯則有類似輸出如下:
$ python test.py ********************************************************************** File "test.py", line 8, in __main__.sum Failed example: sum(1, 9) Expected: 9 Got: 10 ********************************************************************** 1 items had failures: 1 of 2 in __main__.sum ***Test Failed*** 1 failures.