day25 Pyhton學習 約束和異常處理
阿新 • • 發佈:2018-11-17
一.類的約束
約束是對類的約束
有兩種方法:
1.提取一個父類,在父類中給出一個方法,並且在方法中不給出任何程式碼,直接拋異常
class Base: def login(self): raise Exception("你沒有實現login方法()") class Normal(Base): def login(self): pass class Member(Base): def denglu(self): pass class Admin(Base): def login(self): pass
# 專案經理寫的總入口 def login(obj): print("準備驗證碼.......") obj.login() print("進入主頁.......")
n = Normal() m = Member() a = Admin() login(n) login(m) # 報錯. login(a)
在執行到login(m)的時候程式會報錯. 原因是, 此時訪問的login()是父類中的方法. 但是父類中的方法會丟擲一個異常. 所以報錯. 這樣程式設計師就不得不寫login方法了. 從而對子類進行了相應的約束. 在本示例中. 要注意. 我們丟擲的是Exception異常. 而Exception是所有異常的根. 我們無法通過這個異常來判斷出程式是因為什麼報的錯. 所以. 最好是換一個比較專業的錯誤資訊. 最好是換成NotImplementError. 其含義是."沒有實現的錯誤". 這樣程式設計師或者專案經理可以一目瞭然
的知道是什麼錯了. 就好比. 你犯錯了. 我就告訴你犯錯了. 你也不知道哪裡錯了. 這時我告訴你, 你xxx錯了. 你改也好改不是?
2.寫抽象類和抽象方法,這種方案相對來說比上一個麻煩一些.需要大家先引入一個抽象的概念,我們如果寫一個方法,不知道方法的內部應該到底寫什麼,那這個方法就應該是一個抽象方法,如果一個類包含抽象方法,那麼這個類一定是一個抽象類.抽象類是不能有
例項的
在python中編寫一個抽象類比較麻煩. 需要引入abc模組中的ABCMeta和abstractmethod這兩個內容. 來我們看一個例子.from abc import ABCMeta, abstractmethod # 類中包含了抽象方法. 那此時這個類就是個抽象類. 注意: 抽象類可以有普通方法 class IGame(metaclass=ABCMeta): # 一個遊戲到底怎麼玩兒? 你能形容? 流程能一樣麼? @abstractmethod def play(self): pass def turn_off(self): print("破B遊戲不玩了, 脫坑了") class DNFGame(IGame): # 子類必須實現父類中的抽象方法. 否則子類也是抽象類 def play(self): print("dnf的玩兒法") # g = IGame() # 抽象類不能建立物件 dg = DNFGame() dg.play() 通過程式碼我們能發現. 這裡的IGame對DNFGame進行了約束. 換句話說. 父類對子類進行了約束
接下來. 繼續解決我們一開始的問題. from abc import ABCMeta, abstractmethod class Base(metaclass=ABCMeta): @abstractmethod def login(self): pass class Normal(Base): def login(self): pass class Member(Base): def denglu(self): # 這個就沒用了 pass def login(self): # 子類對父類進行實現 pass class Admin(Base): def login(self): pass # 專案經理寫的總入口 def login(obj): print("準備驗證碼.......") obj.login() print("進入主頁.......") n = Normal() m = Member() a = Admin() login(n) login(m) login(a) 總結: 約束. 其實就是父類對子類進行約束. 子類必須要寫xxx方法. 在python中約束的方式和方法有兩種: 1. 使用抽象類和抽象方法, 由於該方案來源是java和c#. 所以使用頻率還是很少的 2. 使用人為丟擲異常的方案. 並且儘量丟擲的NotImplementError. 這樣比較專業, 而且錯誤比較明確.(推薦)
二.異常處理
什麼是異常?異常是程式在執行過程中產生的錯誤,如果程式出現了異常,怎麼處理呢?
def chu(a, b): return a/b try: ret = chu(10, 0) print(ret) except Exception as e: print("除數不能是0") 結果: 除數不能是0 那try...except是什麼意思呢? 嘗試著執行xxxxx程式碼. 出現了錯誤. 就執行except後面的程式碼. 在這個過程中. 當代碼出現錯誤的時候. 系統會產生一個異常物件. 然後這個異常會向外拋. 被except攔截. 並把接收到的異常物件賦值給e. 這裡的e就是異常物件. 那這裡的
Exception是什麼?Exception是所有異常的基類, 也就是異常的跟. 換句話說. 所有的錯誤都是Exception的子類物件. 我們看到的ZeroDivisionError 其實就是Exception的子類. 那這樣寫好像有點問題. Exception表示所有的錯誤. 太籠統了. 所有的錯誤都會被認為是Exception.
當程式中出現多種錯誤的時候, 就不好分類了, 最好是出什麼異常就用什麼來處理. 這樣就更加合理了.所以在try...execpt語句中. 還可以寫更多的except.
給出一個完整的異常處理方法(語法):
try: '''操作''' except Exception as e: '''異常的父類,可以捕獲所有的異常''' else: '''保護不丟擲異常的程式碼, 當try中無異常的時候執⾏''' finally: '''最後總是要執行我'''
解讀: 程式先執行操作, 然後如果出錯了會走except中的程式碼. 如果不出錯, 執行else中的程式碼. 不論出不出錯. 最後都要執行finally中的語句. 一般我們用try...except就夠用了. 頂多加上finally. finally一般用來作為收尾工作.
def add(a, b): ''' 我傳遞兩個整數. 我幫你計算兩個數的和 :param :param a: :param :param b: :return :return: ''' if not type(a) == int and not type(b) == int: # 當程式執行到這句話的時候. 整個函式的呼叫會被中斷. 並向外丟擲一個異常. raise Exception("不是整數, 朕不能幫你搞定這麼複雜的運算.") return a + b # 如果呼叫方不處理異常. 那產生的錯誤將會繼續向外拋. 最後就拋給了使用者 # add("你好", "我叫賽利亞") # 如果呼叫方處理了異常. 那麼錯誤就不會丟給使用者. 程式也能正常進行 try: add("胡辣湯", "滋滋冒油的大腰子") except Exception as e: print("報錯了.自己處理去吧")
當程式執行到raise. 程式會被中斷. 並例項化後面的異常物件. 拋給呼叫方. 如果呼叫方不處理. 則會把錯誤繼續向上丟擲. 最終拋給使用者. 如果呼叫方處理了異常. 那程式可以正常的進行執行.
自定義異常: 非常簡單. 只要你的類繼承了Exception類. 那你的類就是一個異常類. 就這麼簡單.
# 繼承Exception. 那這個類就是一個異常類 class GenderError(Exception): pass class Person: def __init__(self, name, gender): self.name = name self.gender = gender def nan_zao_tang_xi_zao(person): if person.gender != "男": raise GenderError("性別不對. 這裡是男澡堂⼦") p1 = Person("alex", "男") p2 = Person("eggon", "蛋") # nan_zao_tang_xi_zao(p1) # nan_zao_tang_xi_zao(p2) # 報錯. 會丟擲一個異常: GenderError # 處理異常 try: nan_zao_tang_xi_zao(p1) nan_zao_tang_xi_zao(p2) except GenderError as e: print(e) # 性別不對, 這裡是男澡堂子 except Exception as e: print("反正報錯了")
如果是真的報錯了. 我們在除錯的時候, 最好是能看到錯誤源自於哪裡? 怎麼辦呢? 需要引入另一個模組traceback. 這個模組可以獲取到我們每個方法的呼叫資訊. 又被成為堆疊資訊. 這個資訊對我們拍錯是很有幫助的.
import traceback # 繼承Exception. 那這個類就是一個異常類 class GenderError(Exception): pass class Person: def __init__(self, name, gender): self.name = name self.gender = gender def nan_zao_tang_xi_zao(person): if person.gender != "男": raise GenderError("性別不對. 這裡是男澡堂子") p1 = Person("alex", "男") p2 = Person("eggon", "蛋") # nan_zao_tang_xi_zao(p1) # nan_zao_tang_xi_zao(p2) # 報錯. 會丟擲一個異常: GenderError # 處理異常 try: nan_zao_tang_xi_zao(p1) nan_zao_tang_xi_zao(p2) except GenderError as e: val = traceback.format_exc() # 獲取到堆疊資訊 print(e) # 性別不對. 這裡是男澡堂子 print(val) except Exception as e: print("反正報錯了") #性別不對. 這裡是男澡堂子 Traceback (most recent call last): File "D:/python_qishi/day021練習/練習.py", line 66, in <module> nan_zao_tang_xi_zao(p2) File "D:/python_qishi/day021練習/練習.py", line 58, in nan_zao_tang_xi_zao raise GenderError("性別不對. 這裡是男澡堂子") GenderError: 性別不對. 這裡是男澡堂子
當測試程式碼的時候把堆疊資訊打印出來. 但是當到了線上的生產環境的時候把這個堆疊去掉即可.