by 洪培軒
in xxx
¶告訴你那些問題在哪個 scope 被發現的
告訴你程式經歷了哪些大風大浪才死去
# traceback.py
def take(key):
print(dic[key])
def generate_key(code):
key = str(code)+'A'
take(key)
def generate_code(data):
code = data%4
generate_key(code)
def walk_in(data):
generate_code(data)
dic = {'0A': 'plate', '1A': 'spoon'}
walk_in(11)
IndexError
¶錯誤的『名稱』,還有錯誤的『說明』
# wrong.py
a = [123, 345, 789]
print(a[3]) # line 2
下面程式碼希望能夠印出 num 裏面的所有數字,但是程式跑到一半會出錯。
執行後解釋出現的錯誤訊息,並說出程式在哪裡出了問題。
TIPS: 如果不知道出現的 exception 代表什麼意思,可以參考 python 內建的 exception
def div(dividend, divisor):
print("The answer is {}".format(dividend/divisor))
for i in range(5, -1, -1):
for j in range(5, -1, -1):
div(i, j)
更改上面程式碼讓程式可以正常運作
print(int("a"))
print(int("1"))
ggg_abc()
def ggg_abc():
print("in ggg_abc()")
gggg_abc()
print('hello world")
EOL: End of Line
print('hello world!')
if True:
print("ok!")
if True:
print("ok!")
最基本的語法
try:
# do something
except Exception:
# handle the exception
else:
# optional, if no exception happens
try:
print("In try block")
except Exception:
print("In Exception block")
else:
print("No exception")
寫出一個會產生 exception 的程式,並用 try...except...else 接起來
e 會是抓到的那個 exception 的 instance object
# run me!
try:
num = x
except NameError as e:
print(e)
type(e)
看 e 是什麼型別的instance.args
看看這個 object 的 attribute 有哪些# run me!
a = [1, 2, 3]
try:
print(a[100])
num = x
except NameError as e:
print("I'm in NameError! ")
print(e)
except IndexError as e:
print("I'm in IndexError!" )
print(e)
else:
print("I'm in else!")
(把 print(a[100]) 和 num = x 互換看看)
# run me!
a = [1,2,3]
try:
print(a[100])
num = x
except (NameError, IndexError) as e:
print(e)
不論是否產生 exception, 『最後』一定會被執行到的區塊
try:
print("hello!")
x = gggggg
except Exception as e:
print(e)
else:
print("In else")
print("I'm in finally!")
如果把 finally 刪掉會怎樣呢?
比較這兩種程式碼:
def func():
try:
print("hello")
except Exception:
print("in exception")
return
# do something
def func():
try:
print("hello")
except Exception:
print("in exception")
return
finally:
# do something
# run me!
def func():
try:
print("I'm in try block!")
a = bggggggg
except Exception:
print("I'm in except block!")
return
finally:
print("do something")
func()
把 finally 去掉再試試看,會發生什麼事?
丟出一個 exception 給別人接
# run me!
def div(a, b):
if b == 0:
raise ValueError("divisor cannot be zero!")
else: return a/b
num = div(1,0)
print(num)
寫一個 function,function 需要 raise exception
呼叫 function 的時候需要用 try...except 包起來
挑戰題 1 (ex5.py)
實做 小明買醬油 的故事
import random
item_in_shop = {"soybean_sauce": 0, "milk": 4, "salt": 10, "soybean_milk": 3}
items = [item for item in item_in_shop.keys()]
cnt = 5
def buy(item):
# TODO: 補上程式碼和完成邏輯
# tips: 如果東西數量是 0 需要 raise Exception,否則就把物品的數量減 1
print("Mommy! I've bought {} for you!".format(item))
# 買五個隨機的東西
while cnt:
cnt -= 1
index = random.randint(0,3)
item = items[index]
# 想要買的東西是 item,利用 buy() 來買東西
# TODO: 補上程式碼
# tips: 記得用 try...except 包起來
易讀性,減少累贅的判斷式
沒有使用 exception 的時候
def calc_percentage(dividend, divisor):
if divisor == 0:
return -1
else:
return 100.0*dividend/divisor
a1 = calc_percentage(25.0, 100.0)
a2 = calc_percentage(30.0, 56.0)
a3 = calc_percentage(16.0, 49.0)
if a1 == -1:
print("Not vaild divisor")
else:
print("The answer is " + str(a1)+"%")
if a2 == -1:
print("Not vaild divisor")
else:
print("The answer is " + str(a2)+"%")
if a3 == -1:
print("Not vaild divisor")
else:
print("The answer is " + str(a3)+"%")
易讀性,減少累贅的判斷式
使用 exception 的時候
def calc_percentage(dividend, divisor):
if divisor == 0:
raise ZeroDivisionError("Divisor cannot be zero!")
else:
return 100.0*dividend/divisor
a1 = calc_percentage(25.0, 100.0)
a2 = calc_percentage(30.0, 56.0)
a3 = calc_percentage(16.0, 49.0)
print("The answer is " + str(a1)+"%")
print("The answer is " + str(a2)+"%")
print("The answer is " + str(a3)+"%")
避免使用回傳值作為判斷的依據(有時候會忘記,尤其是如果 function 不是自己寫的時候)
沒有使用 exception 的時候
def calc_percentage(dividend, divisor):
if divisor == 0:
return -1
else:
return 100.0*dividend/divisor
answer = calc_percentage(25.0, 0.0)
print("The answer is " + str(answer)+"%")
避免使用回傳值作為判斷的依據(有時候會忘記,尤其是如果 function 不是自己寫的時候)
使用 exception 的時候
def calc_percentage(dividend, divisor):
if divisor == 0:
raise ZeroDivisionError("Divisor cannot be zero!")
else:
return 100.0*dividend/divisor
answer = calc_percentage(25.0, 0.0)
print("The answer is " + str(answer)+"%")
使用已經寫好的 function,隨機取出 list 裏面的資料,請找出程式的問題。
TIPS:多執行幾次看看
註: get_index_random(m, n) 可以得到一個隨機的 index,可以多執行幾次看看問題出在那邊
import random
def get_index_random(m, n):
# Does not expect m to be 1, return -1
if m==1:
return -1
return (n+m+random.randint(0,4))%4
my_list = ['apple', 'banana', 'cherry', 'dog']
for i in range(10):
for j in range(10):
index = get_index_random(i, j)
print(my_list[index])
import random
def get_index_random(m, n):
# Does not expect m to be 1, return error code
if m==1:
return -1
return (n+m+random.randint(0,4))%4
my_list = ['apple', 'banana', 'cherry', 'dog']
for i in range(10):
for j in range(10):
index = get_index_random(i, j)
# ------加上判斷式------
if index==-1:
print("m cannot be 1, try another m value");
# -------------------
else: print(my_list[index])
# run me!
class MyException(Exception):
def __init__(self, err_msg):
self.msg = err_msg
def __str__(self):
return "abc" + self.msg
try:
raise MyException("I'm an exception message!")
except MyException as e:
print("---encountered MyEception---")
print(e)
自己定義一個 exception 叫作 RelationException
在 raise 的時候需要能夠接受 2 個字串,像是這樣 raise RelationException("Mommy", "Daddy")
輸出的時候需要以下列格式印出 (P1 和 P2 是 raise exception 時傳入的參數)
Are you sure that P1 and P2 are in love with each other?
'''
TODO: define a exception class
'''
relation = {'Jason':'Mary', 'Mary':'Jason', 'Jennifer':'Ken', 'Ken':'Jennifer', 'Tina':'Kim', 'Kim':'Tina'}
def check(pa, pb):
if relation[pa] != pb:
# TODO: raise exception
# TIPS: 這個地方會需要 raise 兩種 exception
try:
p1 = input("Please enter the first person: ")
p2 = input("Please enter the second person: ")
check(p1, p2)
print("{} and {} are in love with each other!".format(p1, p2))
except '''what exception?''' as e:
print(e)
輸入:
Jason
Mary
輸出:
Jason and Mary are in love with each other!
輸入:
Jen
Ducky
輸出:
No relation found
Are you sure that Jen and Ducky are in love with each other?
輸入:
Jason
Jennifer
輸出:
Are you sure that Jason and Jennifer are in love with each other?
I'm _____ (status: xxx), need to ......
___
和xxx
和...
因為不同狀況會是不同字串import random
'''
TODO: define exception classes
'''
def check(man):
# TODO: in what condition need to raise exception?
def play(man):
print("playing...")
man["hunger"] -= 15
man["water"] -= 15
man["mood"] += 5
check(man)
def eat(man):
print("eating...")
man["hunger"] += 5
check(man)
def drink(man):
print("drinking...")
man["water"] += 5
check(man)
actionList = [play, eat, drink]
child = {"hunger": 20, "water": 20, "mood": 20}
while True:
cnt -= 1
rand = random.randint(0,2)
# TODO: 把隨機的動作用 try...except 包起來
actionList[rand](child)
# TIPS: 記得只要抓到 exception 之後就要 break 了,不然會造成無窮迴圈
playing...
drinking...
drinking...
drinking...
playing...
I'm hungry (status: -10), need to eat!
eating...
參考墜樓的故事,把故事敘述用程式碼表達
小明在 106 樓看風景,不小心腳一滑從 106 樓掉下去。
這時候會觸發大樓的安全機關(FallDownException),但是因為小明太胖了所以第一層安全機關會被突破。
好險大樓還有第二層安全機關(FallDownStrongerException),最後終於把小明接住了!
定義兩個 exception,分別是
定義一個函式
# TODO: 按照敘述定義出兩個 Exception
def slip(floor):
try:
while floor:
floor -= 1
print("現在在 {} 樓".format(floor))
if floor == 80:
# TODO: 要 raise 一個 exception
except '''TODO: 要用一個 exception 接''' as e:
print(e)
print("突破機關!")
while floor:
floor -= 1
print("現在在 {} 樓".format(floor))
if floor == 5:
# TODO: 要 raise 一個 exception
# TODO: 用 try...except 把 slip(106) 包起來
slip(106)