day19--面向物件之約束
阿新 • • 發佈:2018-11-12
一、上次內容回顧
1、反射
1、hasattr(物件,屬性(字串))
2、getattr(物件,屬性(字串))
3、setattr(物件,屬性,值)
4、delattr(物件,屬性)
2、issubclass ,type , isinstance
issunclass,判斷xxx物件或者類是否是xxx的子類
type:獲取xxx物件的資料型別
isinstance:判斷xxx物件是否是xxx型別(向上查詢)
3、方法和
類外面寫的函式都是函式
在類中:
例項方法:
1、類名.方法() 函式
2、物件.方法() 方法
類方法:
都是方法
靜態方法
都是函式
4、md5
特點:不可逆
加鹽
import hashlib
obj = hashlib.md5(鹽)
obj.update(byte型別的明文)
result = obj.hetdidest()
二、類的約束
類的約束,主要是為了定義一個父類,約束下面子類必須按照規則
完成父類中的功能(父類中的功能為空白)
在python中有兩種辦法來解決這樣的問題
1、提取父類。然後再父類中定義好方法。在這個方法中什麼都不用幹,拋一個異常
就可以了。這樣所有的子類都必須重寫這個方法。否則,訪問的時候報錯
2、使用元類來描述父類。在元類中給出一個抽象方法。這樣子類就不得不給出抽象方法
的具體實現。也可以起到約束的效果
第一套方案示例:
class Base: def login(self): raise Exception("你沒有實現login⽅法()") class Normal(Base): def login(self): pass class Member(Base): def denglu(self): pass classAdmin(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異常,是所有異常的根。我們無法通過這個異常來判斷出程式
是因為什麼報的錯。所以,最好是換一個比較專業的錯誤資訊,換成NotlmplementError
意為:沒實現的錯誤
第二套方案:寫抽象類和抽象方法
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 ret = chu(10, 0) print(ret) 結果: Traceback (most recent call last): File "/Users/sylar/PycharmProjects/oldboy/⾯向物件/day05.py", line 100, in <module> ret = chu(10, 0) File "/Users/sylar/PycharmProjects/oldboy/⾯向物件/day05.py", line 98, in chu return a/b ZeroDivisionError: division by zero 什麼錯誤呢. 除法中除數不能是0. 那如果真的出了這個錯. 你把這⼀堆資訊拋給客戶 麼? 肯定不能. 那如何處理呢? 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
import traceback # 計算a+b def cul(a, b): if (type(a) == int or type(a) == float) and (type(b) == int or type(b) == float): return a + b else: # 在這裡有兩種方案. 1. 直接返回 , 2. 丟擲異常 # raise 丟擲 Exception 錯誤和異常,所有錯誤的根 raise Exception("我要的不是這個. 你應該我傳遞int或者float") try: print(cul(1, "胡辣湯")) # 加上異常的處理 except Exception as e: # 獲取到錯誤資訊. 我們需要訪問堆疊資訊 print(traceback.format_exc()) # 獲取堆疊資訊 print("出現了錯誤") 我們出現的錯誤. python中沒有給出具體的記錄, 慎用. 名字一定要符合規範 JackException 一個讓人無從得知的自定義異常,切記慎用自定義異常 自定義異常 class GenderException(Exception): pass class Person: def __init__(self, name, gender): self.name = name self.gender = gender # 洗澡 -> 男的進男浴室 def goto_nan_yushi(self): if self.gender != "男": raise GenderException("性別不對") # 除了名字以外都是父類中的Exception else: print("歡迎光臨.") try: p2 = Person("wusir", "女") p2.goto_nan_yushi() p1 = Person("alex", "男") p1.goto_nan_yushi() except GenderException as e: print("你去男澡堂子幹嘛?") except Exception as e: print("其他錯誤")
四、日誌處理
import logging # filename: ⽂件名 # format: 資料的格式化輸出. 最終在⽇志⽂件中的樣⼦ # 時間-名稱-級別-模組: 錯誤資訊 # datefmt: 時間的格式 # level: 錯誤的級別權重, 當錯誤的級別權重⼤於等於leval的時候才會寫⼊⽂件 logging.basicConfig(filename='x1.log', format='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S', level=30) # 當前配置表示 0以上的分數會被寫⼊⽂件 # CRITICAL = 50 # FATAL = CRITICAL # ERROR = 40 # WARNING = 30 # WARN = WARNING # INFO = 20 # DEBUG = 10 # NOTSET = 0 logging.critical("我是critical") # 50分. 最貴的 logging.error("我是error") # 40分 logging.warning("我是warning") logging.info("我是info") logging.debug("我是debug") logging.log(1, "我什麼都不是") import traceback try: print(1/0) except Exception: logging.error(traceback.format_exc()) # 用法 print("出錯了") import logging # 建立⼀個操作⽇志的物件logger(依賴FileHandler) # open() file_handler = logging.FileHandler('zuo.log', 'a', encoding='utf-8') file_handler.setFormatter(logging.Formatter(fmt="%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s")) logger1 = logging.Logger('qq', level=20) logger1.addHandler(file_handler) # 把檔案助手和日誌物件繫結 logger1.error('我是A系統出錯了') # 記錄日誌 # 再建立⼀個操作⽇志的物件logger(依賴FileHandler) file_handler2 = logging.FileHandler('you.log', 'a', encoding='utf-8') file_handler2.setFormatter(logging.Formatter(fmt="%(asctime)s - %(name)s -%(levelname)s -%(module)s: %(message)s")) logger2 = logging.Logger('B系統', level=20) logger2.addHandler(file_handler2) import traceback try: print(1/0) except Exception: logger2.critical(traceback.format_exc()) print("出錯了. 請聯絡管理員") print("程式繼續知悉個") from types import MethodType, FunctionType class Foo: @classmethod def func1(self): pass @staticmethod def func2(self): pass def func3(self): pass def func4(self): pass obj = Foo() lst.append(obj.func4) for item in lst: print(isinstance(item, MethodType))