學習python,從入門到放棄(27)
學習python,從入門到放棄(27)
繼承下的派生實際應用
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, cls=MyJsonEncoder) print(res)
json 不能序列化 python 所有的資料型別,只能是一些基本資料型別。所以我們可以使用兩種方法來實現其他的資料型別的序列化。
-
手動將不能序列化的型別先轉字串
{'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__)
面向物件三大特性之封裝
封裝就是將類中的某些名字'隱藏'起來 不讓外界直接呼叫,隱藏的目的是為了提供專門的通道去訪問,在通道內可以新增額外的功能。
在變數名的前面加上兩個下劃線___變數名
即可實現封裝,封裝的功能只在類定義階段才能生效,在類中封裝其實也不是絕對的,僅僅是做了語法上的變形而已,當需要呼叫時,只要使用_類名__變數名
的形式即可呼叫。我們雖然指定了封裝的內部變形語法 但是也不能直接去訪問,看到了就表示這個屬性需要通過特定的通道(介面)去訪問。
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 = '清華大學' __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) # 逆來順受
將資料隱藏起來就限制了類外部對資料的直接操作,然後類內應該提供相應的介面來允許類外部間接地操作資料,介面之上可以附加額外的邏輯來對資料的操作進行嚴格地控制,目的的是為了隔離複雜度,例如ATM程式的取款功能,該功能有很多其他功能組成。
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() 。
-
注意事項
# 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,無法例項化
-
鴨子理論
要你看著像鴨子,走路像鴨子,說話像鴨子,那麼你就是鴨子。
得到記憶體或者硬碟物件之後 只要想讀取資料就呼叫read 想寫入資料就呼叫write 不需要考慮具體的物件是誰。
class Memory(object): def read(self): pass def write(self): pass class Disk(object): def read(self): pass def write(self): pass
面向物件之反射
反射是指程式可以訪問、檢測和修改本身狀態或者行為的一種能力。簡單來說就是通過字串來操作物件的資料和功能。
-
四個方法
hasattr()
:判斷物件是否含有字串對應的資料或者功能getattr()
:根據字串獲取對應的變數名或者函式名setattr()
:根據字串給物件設定鍵值對(名稱空間中的名字)delattr()
:根據字串刪除物件對應的鍵值對(名稱空間中的名字) -
實際應用
判斷 Student 名稱空間中是否含有使用者指定的名字,如果有則取出展示。
只要在看到關鍵字物件和字串(使用者輸入、自定義、指定) 那麼大概率用反射可以完成。
class Student(object): school = '清華大學' def get(self): pass 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('類中沒有該名字')
-
利用反射獲取配置檔案中的配置資訊
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()