AssertionError
這篇文章是閱讀Asabeneh的30 Days Of Python: Day 17 - Exception Handling後的學習筆記與心得。
這章的原文中還有提到一些元組(tuple)和字典(dictionary)的操作,我想放在Day 18再寫,相對的,原文中沒有提到像是JavaScript中throw new Error()
的操作,我參考了這篇文章加入今天的內容,當中一開頭提到:
語法錯誤(syntax error)像下方的print
函式少了右括號關閉:
print(0/0
這類型的錯誤是不能用今天這章的例外(exceptions)處理來捕捉。
在JavaScript(以下簡稱JS)中會使用try...catch
來避免一個錯誤發生整個程式就中斷了,在Python裡,則是使用try...except
來做到這件事:
try:
# 執行這塊程式碼
except:
# try有錯誤發生時,執行這塊程式碼
else:
# try結束沒有錯誤的話,執行這塊程式碼
finally:
# 不管過程中是否有錯誤,最後一定會執行這塊程式碼
跟JS的 try...catch
相似,帶個例子來看:
try:
print(10 + "5") # results TypeError
except:
print("有錯誤") # 有錯誤
print(10 + "5")
# TypeError: unsupported operand type(s) for +: 'int' and 'str'
print("hello") # won't be executed
這樣會在終端(terminal)印出有錯誤
,並可以看到在下一段執行同樣的程式碼會觸發TypeError
,而且因為沒做例外處理,程式就中斷了,最後一行的print
不會被執行到。
但可以注意到,原本Python內建的錯誤提示被我們自行撰寫的「有錯誤」給取代掉了,雖然可以透過,except Exception as err
的方式拿到原本Python的錯誤描述,但描述中會少了error type,JS的話則會帶有error type的資訊。
Python中可以指定要抓取的錯誤類型,這點是JS中沒有的;修改一下上面的例子:
try:
print(10 + "5") # results TypeError
except ValueError:
print("有錯誤") # won't be executed
print("但程式沒有中斷")
try
當中,我們觸發的是TypeError
但只有做ValueError
的處理。TypeError
的例外,讓程式能順利執行完:try:
print(10 + "5") # results TypeError
except ValueError:
print("有錯誤") # won't be executed
except TypeError:
print("型別錯誤") # 型別錯誤
print("但程式沒有中斷") # 但程式沒有中斷
在JS的例外處理中沒有這個行為,在這個子句(clause)中的程式碼會在try
子句完成後沒有發生錯誤才會執行:
do_math() -> float
那個箭頭是型別提示,作用就像TypeScript標出函式的輸出那樣。 -- 參考這則回答
from random import random
import math
error_log = []
def do_math() -> float:
id = math.floor(random() * 10000)
error_log.append(f"{id} - do_math: start")
result = 0
try:
num = int(input("Enter a number: "));
result += math.pow(num, 2);
except Exception as err:
error_log.append(f"{id} - do_math: error - {err}")
print(f"Exception: {err}")
return do_math();
else:
error_log.append(f"{id} - do_math: end")
return result;
foo = do_math()
print(foo)
print(error_log)
執行後操作及輸出如下(串列輸出有手動排版過):
Enter a number: g
Exception: invalid literal for int() with base 10: 'g'
Enter a number: 2
4.0
[
'1068 - do_math: start',
"1068 - do_math: error - invalid literal for int() with base 10: 'g'",
'7000 - do_math: start',
'7000 - do_math: end'
]
int()
函式中。foo
並印出error_log
中可以看到do_math
執行了兩次,一次是錯誤收尾,另一次則是順利結束函式。這個finally
不是指這篇文章的結束?,JS的錯誤處理中也有這個關鍵字(keyword),如文章開頭提到,這個子句(clause)會在函式最後執行:
把上面 else
例子中推送 error_log
的操作移到finally
中:
from random import random
import math
error_log = []
def do_math() -> float:
id = math.floor(random() * 10000)
error_log.append(f"{id} - do_math: start")
result = 0
try:
num = int(input("Enter a number: "));
result += math.pow(num, 2);
except Exception as err:
error_log.append(f"{id} - do_math: error - {err}")
print(f"Exception: {err}")
return do_math();
else:
return result;
finally:
error_log.append(f"{id} - do_math: end")
foo = do_math()
print(foo)
print(error_log)
執行後操作及輸出如下(串列輸出有手動排版過):
Enter a number: h
Exception: invalid literal for int() with base 10: 'h'
Enter a number: 3
9.0
[
'4846 - do_math: start',
"4846 - do_math: error - invalid literal for int() with base 10: 'h'",
'5201 - do_math: start',
'5201 - do_math: end',
'4846 - do_math: end'
]
else
的例子在error_log
的部份不同,第一次給錯誤型別的的do_math
(4806),並沒有隨著錯誤發生而結束,在do_math
(5201)結束後,do_math
(4806)的finally
還是有執行推送log。Python中可以使用raise
來產生例外,中斷程式:
month = int(input("Entering a number between 1 to 12: "))
if month < 1 or month > 12:
raise Exception("Valid value is between 1 to 12. Entered value: {}, is invalid".format(month))
print("The value you entered is: {}".format(month))
AssertionError
使用assert
依據判斷式在符合(True
)時繼續程式,在不符合(False
)時拋出AssertionError
:
month = int(input("Entering a number between 1 to 12: "))
assert(month >= 1 and month <= 12), "Valid value is between 1 to 12. Entered value: {}, is invalid".format(month)
print("The value you entered is: {}".format(month))
Python中函式的宣告不像JS會hoisting,是由上而下執行,參考這則回答中的例子;若是在宣告前一函式前的段落就執行該函式會產生NameError
。
在JS中嘗試相同的操作並不會產生錯誤:
function print_sum(a, b) {
console.log(sum_numbers(a, b))
}
print_sum(2, 4) // 6
function sum_numbers(a, b) {
return a + b
}
最後寫了這個例子來總結這章的學習。