面向物件之封裝和多型
面向物件
- 繼承下的派生實際應用
- 面向物件三大特性之封裝
- 面向物件三大特性之多型
- 反射
繼承下的派生實際應用
應用案例
import datetime
import json
d1 = {'t1':datetime.datetime.today(),'t2':datetime.date.today()}
print(d1)
print(json.dumps(d1)) # 不能正常列印,會報錯
解決辦法
注意:可以先檢視json可以序列化的物件 eg:
1.轉字串,暴力直接
d2 = {'t1':str(datetime.datetime.today()),'t2':str(datetime.date.today())} print(json.dumps(d2))
2.研究json方法,重寫序列化方法
import datetime import json class MyJsonEncoder(json.JSONEncoder): # 重寫default方法 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方法繼續執行 防止有其他額外操作)
面向物件三大特性之封裝
將類中的某些名字隱藏起來 不讓外界直接呼叫
隱藏的目的是為了提供專門的通道去訪問 在通道內可以新增額外的功能
程式碼示例如下:
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('tony', 18) stu1.check_info() stu1.set_info('kevin',28) stu1.check_info() stu1.set_info('','nb') print(stu1.__dict__) # 通過列印dict就可以看到stu1中的name已經更改
將資料隱藏起來就限制了類外部對資料的直接操作,然後類內應該提供相應的介面來允許類外部間接地操作資料,
介面之上可以附加額外的邏輯來對資料的操作進行嚴格地控制
目的的是為了隔離複雜度,例如ATM程式的取款功能,該功能有很多其他功能組成
比如插卡、身份認證、輸入金額、列印小票、取錢等,而對使用者來說,只需要開發取款這個功能介面即可,其餘功能都可以隱藏起來
property
property就是將方法偽裝成資料
有時候很多資料需要經過計算才可以獲得
但是有些資料給我們的感覺應該屬於資料而不是功能
比如:BMI指數>>>:應該屬於人的資料而不是人的功能
程式碼示例:
1.不偽裝
class Person(object):
def __init__(self, name, height, weight):
self.__name = name
self.height = height
self.weight = weight
def BMI(self):
print('%s的BMI指數是:%s' % (self.__name, self.weight / (self.height ** 2)))
p1 = Person('jason', 1.8, 80)
p1.BMI()
2.偽裝
class Person(object):
def __init__(self, name, height, weight):
self.__name = name
self.height = height
self.weight = weight
@property
def BMI(self):
return '%s的BMI指數是:%s' % (self.__name, self.weight / (self.height ** 2))
p1 = Person('jason', 1.8, 80)
print(p1.BMI)
p2 = Person('eason',1.9,85)
print(p2.BMI)
面向物件三大特性之多型
什麼是多型: 一種事物的多種形態
eg:
水 固態 液態 氣態
動物 貓、狗、豬
程式碼體現:
class Animal(object):
def speak(self):
pass
class Cat(Animal):
def miao(self):
print('喵喵喵')
class Dog(Animal):
def wang(self):
print('汪汪汪')
class Pig(Animal):
def heng(self):
print('哼哼哼')
上述場景下 雖然體現了事物的多型性 但是並沒有完整的體現出來
因為現在不同的形態去叫 需要呼叫不同的方法 不夠一致
只要你是動物 那麼你想要說話 就應該呼叫一個相同的方法 這樣便於管
理
程式碼體現如下:
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('哼哼哼')
這樣他們呼叫的時候就都是speak方法了
多型性的好處在於增強了程式的靈活性和可擴充套件性,比如通過繼承Animal類建立了一個新的類,例項化得到的物件obj,可以使用相同的方式使用 obj.speak()
雖然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,無法例項化
鴨子型別
由多型性衍生出一個鴨子型別理論
只要你看著像鴨子 走路像鴨子 說話像鴨子 那麼你就是鴨子!!!
實戰案例:
"""
在linux系統中有一句話>>>:一切皆檔案!!!
記憶體可以存取資料
硬碟可以存取資料
...
那麼多有人都是檔案
"""
class Memory(object):
def read(self):
pass
def write(self):
pass
class Disk(object):
def read(self):
pass
def write(self):
pass
得到記憶體或者硬碟物件之後 只要想讀取資料就呼叫read 想寫入資料就調
用write 不需要考慮具體的物件是誰
反射
什麼是反射
專業解釋:指程式可以訪問、檢測和修改本身狀態或者行為的一種能力
大白話:其實就是通過字串來操作物件的資料和功能
這裡要注意:在python中一切皆物件
反射需要掌握的四個方法
hasattr():判斷物件是否含有字串對應的資料或者功能
getattr():根據字串獲取對應的變數名或者函式名
setattr():根據字串給物件設定鍵值對(名稱空間中的名字)
delattr():根據字串刪除物件對應的鍵值對(名稱空間中的名字)
反射實際應用
class Student(object):
school = '清華大學'
def get(self):
pass
print(Student.__dict__)
'''字串的school 跟 變數名school差距大不大? 本質區別'''
# 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('類中沒有該名字')
def index():
pass
obj = Student()
setattr(obj, '血量', 10000)
setattr(obj, '功能', index)
print(obj.__dict__)
delattr(obj, '功能')
print(obj.__dict__)
setattr
setattr(Student,'level','貴族學校')
def index():
pass
setattr(Student, '功能', index)
什麼時候使用反射 可以記固定的口訣
以後只要在業務中看到關鍵字
物件 和 字串(使用者輸入、自定義、指定) 那麼肯定用反射
反射案例
# 利用反射獲取配置檔案中的配置資訊
"""一切皆物件 檔案也是物件"""
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()