1. 程式人生 > >day25 Pyhton學習 約束和異常處理

day25 Pyhton學習 約束和異常處理

一.類的約束

  約束是對類的約束

  有兩種方法:

  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: 性別不對. 這裡是男澡堂子

當測試程式碼的時候把堆疊資訊打印出來. 但是當到了線上的生產環境的時候把這個堆疊去掉即可.