面向對象三大特性之封裝、多態
目錄
-
繼承下的派生實際應用
-
面向對象三大特性之封裝
-
面向對象三大特性之多態
-
反射
內容
1.繼承下的派生實際應用
import datetime import json class MyJsonEncoder(json.JSONEncoder): def default(self, o): # 形參o就是即將要被序列化的數據對象 # print('重寫了', o) '''將o處理成json能夠序列化的類型即可''' if isinstance(o,datetime.datetime): return o.strftime('%Y-%m-%d %X') elif isinstance(o, datetime.date): return o.strftime('%Y-%m-%d') return super().default(o) # 調用父類的default(讓父類的default方法繼續執行 防止有其他額外操作)
d1 = {'t1': datetime.datetime.today(), 't2': datetime.date.today()} res = json.dumps(d1) print(res)
問題:json能否序列化d1字典里的values?
列印結果為:
TypeError: Object of type 'datetime' is not JSON serializable
得出結論:json不能序列化python所有的數據類型 只能是一些基本數據
那麼如何將values轉成字元串?
方法一:手動將不能序列化的類型轉成字元串 str
{'t1': str(datetime.datetime.today()), 't2': str(datetime.date.today())}
方法二:研究json源碼並重寫序列化方法
研究源碼發現報錯的方法叫default
raise TypeError("Object of type '%s' is not JSON serializable" % o.__class__.__name__)
我們可以寫一個類繼承JSONEncoder然後重寫default方法
正確寫法:
d1 = {'t1': datetime.datetime.today(), 't2': datetime.date.today()} res = json.dumps(d1, cls=MyJsonEncoder) print(res)
2.面向對象三大特性之封裝
封裝的含義:將類中的某些名字’隱藏’起來 不讓外界直接調用
隱藏的目的是為了提供專門的通道去訪問 在通道內可以添加額外的功能
程式碼實操:
class Student(object): school = '清華大學' __label = '逆來順受' # 由於python崇尚自由 所以並沒有真正的隱藏 而是自動轉換成了特定的語法 def __init__(self, name, age): self.name = name self.age = age def choose_course(self): print('%s正在選課'%self.name) stu1 = Student('jason', 18) print(stu1.school) # 清華大學 print(stu1.name) # jason print(stu1.age) # 18 print(stu1.__label) # 報錯 print(Student.__dict__) # '_Student__label': '逆來順受' print(Student._Student__label) # 逆來順受 print(stu1._Student__label) # 逆來順受
如何封裝名字:__變數名
封裝的功能只在類定義階段才能生效!!!
我們雖然指定了封裝的內部變形語法 但是也不能直接去訪問 需要通過特定的通道(介面)去訪問
class Student(object): __school = '清華大學' def __init__(self, name, age): self.__name = name self.__age = age # 專門開設一個訪問學生數據的通道(介面) def check_info(self): print(""" 學生姓名:%s 學生年齡:%s """ % (self.__name, self.__age)) # 專門開設一個修改學生數據的通道(介面) def set_info(self,name,age): if len(name) == 0: print('用戶名不能為空') return if not isinstance(age,int): print('年齡必須是數字') return self.__name = name self.__age = age stu1 = Student('jason', 18) stu1.check_info() stu1.set_info('jasonNB',28) stu1.check_info() stu1.set_info('','haha')
將數據隱藏起來就限制了類外部對數據的直接操作,然後類內應該提供相應的介面來允許類外部間接地操作數據,介面之上可以附加額外的邏輯來對數據的操作進行嚴格地控制
目的的是為了隔離複雜度,例如ATM程式的取款功能,該功能有很多其他功能組成
比如插卡、身份認證、輸入金額、列印小票、取錢等,而對使用者來說,只需要開發取款這個功能介面即可,其餘功能我們都可以隱藏起來
property
property就是將方法偽裝成數據
有時候很多數據需要經過計算才可以獲得 但是這些數據給我們的感覺應該屬於數據而不是功能
體質指數(BMI)=體重(kg)÷身高^2(m)
BMI指數>>>:應該屬於人的數據而不是人的功能
class Person(object): def __init__(self, name, height, weight): self.__name = name self.height = height self.weight = weight @property def BMI(self): # print('%s的BMI指數是:%s' % (self.name, self.weight / (self.height ** 2))) return '%s的BMI指數是:%s' % (self.__name, self.weight / (self.height ** 2)) p1 = Person('jason', 1.83, 77) # p1.BMI() # 22.9 print(p1.BMI) p2 = Person('eason',1.90,85) # p2.BMI() # 23.5 print(p2.BMI) p3 = Person('xd',1.85,100) # p3.BMI() # 29.2 print(p3.BMI) p4 = Person('xd',1.5,34) # p4.BMI() # 15.1 print(p4.BMI)
面向對象三大特性之多態
多態:一種事物的多種形態
多態性:
class Animal(object): def speak(self): pass class Cat(Animal): def speak(self): print('喵喵喵') class Dog(Animal): def speak(self): print('汪汪汪') class Pig(Animal): def speak(self): print('哼哼哼')
上述場景下 雖然體現了事物的多態性 但是並沒有完整的體現出來
因為現在不同的形態去叫 需要調用不同的方法 不夠一致
只要你是動物 那麼你想要說話 就應該調用一個相同的方法 這樣便於管理
c1 = Cat() d1 = Dog() p1 = Pig() c1.speak() d1.speak() p1.speak()
多態性的好處在於增強了程式的靈活性和可擴展性,比如通過繼承Animal類創建了一個新的類,實例化得到的對象obj,可以使用相同的方式使用obj.speak()
面向對象的多態性也需要python程式設計師自己去遵守
雖然python推崇的是自由 但是也提供了強制性的措施來實現多態性
import abc # 指定metaclass屬性將類設置為抽象類,抽象類本身只是用來約束子類的,不能被實例化 class Animal(metaclass=abc.ABCMeta): @abc.abstractmethod # 該裝飾器限制子類必須定義有一個名為talk的方法 def talk(self): # 抽象方法中無需實現具體的功能 pass class Person(Animal): # 但凡繼承Animal的子類都必須遵循Animal規定的標準 def talk(self): pass p1=Person() # 若子類中沒有一個名為talk的方法則會拋出異常TypeError,無法實例化
由多態性衍生出一個鴨子類型理論:
class Memory(object): def read(self): pass def write(self): pass class Disk(object): def read(self): pass def write(self): pass
得到記憶體或者硬碟對象之後 只要想讀取數據就調用read 想寫入數據就調用write 不需要考慮具體的對象是誰
反射
專業解釋:指程式可以訪問、檢測和修改本身狀態或者行為的一種能力
大白話:其實就是通過字元串來操作對象的數據和功能
反射需要掌握的四個方法:
hasattr():判斷對象是否含有字元串對應的數據或者功能
getattr():根據字元串獲取對應的變數名或者函數名
setattr():根據字元串給對象設置鍵值對(名稱空間中的名字)
delattr():根據字元串刪除對象對應的鍵值對(名稱空間中的名字)
反射實際應用:
class Student(object): school = '清華大學' def get(self): pass
編寫一個小程式 判斷Student名稱空間中是否含有用戶指定的名字 如果有則取出展示
guess_name = input('請輸入你想要查找的名字>>>:').strip() # 不使用反射不太容易實現 print(hasattr(Student, 'school')) # True print(hasattr(Student, 'get')) # True print(hasattr(Student, 'post')) # False print(getattr(Student, 'school')) # 清華大學 print(getattr(Student, 'get')) # <function Student.get at 0x10527a8c8> guess_name = input('請輸入你想要查找的名字>>>:').strip() if hasattr(Student, guess_name): target_name = getattr(Student, guess_name) if callable(target_name): print('類中有一個功能名字是%s'%guess_name,target_name) else: print('類中有一個數據名字是%s'%guess_name,target_name) else: print('類中沒有該名字') setattr(Student,'level','貴族學校') print(Student.__dict__)
以後只要在業務中看到關鍵字:對象 和 字元串(用戶輸入、自定義、指定) 那麼肯定用反射
反射實際案例
利用反射獲取配置文件中的配置資訊:
import settings dir(settings) # 獲取對象中所有可以使用的名字 getattr(settings, 'NAME') class FtpServer: def serve_forever(self): while True: inp=input('input your cmd>>: ').strip() cmd,file=inp.split() if hasattr(self,cmd): # 根據用戶輸入的cmd,判斷對象self有無對應的方法屬性 func=getattr(self,cmd) # 根據字元串cmd,獲取對象self對應的方法屬性 func(file) def get(self,file): print('Downloading %s...' %file) def put(self,file): print('Uploading %s...' %file) obj = FtpServer() obj.serve_forever()