python面向物件(3)
阿新 • • 發佈:2022-04-09
python面向物件(3)
繼承下的派生實際應用
import datetime import json class MyJsonEncoder(json.JSONEncoder): def default(self, o): # 形參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) """ TypeError: Object of type 'datetime' is not JSON serializable json不能序列化python所有的資料型別 只能是一些基本資料型別 json.JSONEncoder 1.手動將不能序列化的型別先轉字串 {'t1': str(datetime.datetime.today()), 't2': str(datetime.date.today())} 2.研究json原始碼並重寫序列化方法 研究原始碼發現報錯的方法叫default raise TypeError("Object of type '%s' is not JSON serializable" % o.__class__.__name__) 我們可以寫一個類繼承JSONEncoder然後重寫default方法 """
面向物件三大特徵之封裝
# 封裝的含義 將類中的某些名字'隱藏'起來 不讓外界直接呼叫 隱藏的目的是為了提供專門的通道去訪問 在通道內可以新增額外的功能 # 封裝的概念 在python中用雙下劃線,開頭的方式將屬性隱藏起來(設定成私有的) 但其實這只是一種變形操作,而且僅僅在類定義階段會發生變形 類中所有雙下劃線開頭的如__x都會在類定義的時候自動形成:_類名__x的形式 # 程式碼示例: 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(Student.__dict__) """ 如何封裝名字 在變數名的前面加上兩個下劃線__ 封裝的功能只在類定義階段才能生效!!! 在類中封裝其實也不是絕對的 僅僅是做了語法上的變形而已 __變數名 >>> _類名__變數名 我們雖然指定了封裝的內部變形語法 但是也不能直接去訪問 看到了就表示這個屬性需要通過特定的通道(介面)去訪問 """
通過介面呼叫
# 封裝的真諦在於明確地區分內外,封裝的屬性可以直接在內部使用,而不能在外部直接使用,然而定義 # 屬性的目的終歸是要用,外部想要用類隱藏的屬性,需要我們為其開闢介面 1,封裝資料 將資料隱藏起來不是目的,隱藏起來然後提供操作該資料的介面,然後我們可以在 '''介面上附加對返資料操作的限制,以此完成對資料屬性的嚴格控制 類比於電腦上的各種配件,就是被封裝的資料,我們可以通過操作來改變電腦硬體的配置, 例如我們要加一塊記憶體條,只需要知道介面,頻率等引數,而不用知道記憶體條是什麼材質的, 也不用知道記憶體條內部顆粒的構成''' class Teacher: def __init__(self,name,age): self.__name = name self.__age = age self.set_info(name,age) def tell_info(self): print('name:%s,age:%s' %(self.__name,self.__age)) def set_info(self,name,age): if not isinstance(name,str): raise TypeError("姓名必須是字串型別") if not isinstance(age,int): raise TypeError("年齡必須是整數型別") self.__name = name self.__age = age t = Teacher('jason',18) t.tell_info() t.set_info('kevin',28) t.tell_info() 2,封裝方法:目的是隔離複雜度 就像我們玩電腦一樣,我們不需要知道電腦的工作原理,只需要知道他是怎麼用的就可以了 """ 將資料隱藏起來就限制了類外部對資料的直接操作,然後類內應該提供相應的介面來允許類外部間接地操作資料, 介面之上可以附加額外的邏輯來對資料的操作進行嚴格地控制 目的的是為了隔離複雜度,例如ATM程式的取款功能,該功能有很多其他功能組成 比如插卡、身份認證、輸入金額、列印小票、取錢等,而對使用者來說,只需要開發取款這個功能介面即可,其餘功能我們都可以隱藏起來 """
property
# property 的定義
Property 是 Python 類的一個內建的 裝飾器. @property 的作用是 將一個方法, 變為屬性來呼叫
# 訪問類的私有屬性
class Foo:
def __init__(self, score):
self.__score = score
@property # 將方法裝飾為屬性
def show_score(self):
return self.__score
s = Foo(66)
# 在寫法上更加優雅, 不用多寫 "方法()" 沒用的括號
print(s.show_score)
property擴充套件
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)
property示例
'''封裝一個Book的類, 主要用來描述書籍的名稱, 價格和評論. 要求能用 property 裝飾器實現對 Book 的例項屬性 set 和 get 方法進行定義. 即讓例項物件可以用屬性的方式來操作方法/屬性'''
class Book:
def __init__(self, name, price, comment=None):
self.__name = name
self.__price = price
self.__comment = comment
# 傳統方法對price屬性進行 設定和訪問
def get_price(self):
return self.__price
def set_price(self, value):
self.__price = value
# price = property(get_price, set_price)
# @property 裝飾器對 comment 屬性進行 設定和訪問
@property
def my_comment(self):
# 預設是 getter, 需要return 或 yeild
return self.__comment
@my_comment.setter
def my_comment(self, value):
# 檢驗
if not isinstance(value, float):
raise ValueError("must be a decimal number")
if value < 0 or value > 1000:
raise ValueError("0 ~ 1000")
self.__comment = value
@my_comment.deleter
def my_comment(self):
print("delete this atrr...")
面向物件三大特性之多型
# 什麼是多型
一種事物的多種形態
eg:
水 固態 液態 氣態
動物 貓、狗、豬
# 多型性
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('哼哼哼')
"""
上述場景下 雖然體現了事物的多型性 但是並沒有完整的體現出來
因為現在不同的形態去叫 需要呼叫不同的方法 不夠一致
只要你是動物 那麼你想要說話 就應該呼叫一個相同的方法 這樣便於管理
"""
多型性的好處在於增強了程式的靈活性和可擴充套件性,比如通過繼承Animal類建立了一個新的類,例項化得到的物件obj,可以使用相同的方式使用obj.speak()
雖然python推崇的是自由 但是也提供了強制性的措施來實現多型性
不推薦使用
多型的鴨子型別
# python推崇的是鴨子型別
# linux 一切皆是檔案(對於檔案操作一個讀,一個寫)
# 通過多型統一的思想格式把cpu 記憶體 txt檔案做的像檔案,對其進行操作
class Cpu:
def read(self):
print('cpu read')
def write(self):
print('cpu write')
class Mem: # 記憶體
def read(self):
print('mem read')
def write(self):
print('mem write')
class Txt:
def read(self):
print('txt read')
def write(self):
print('txt write')
# 統一使用
obj1 = Cpu()
obj2 = Mem()
obj3 = Txt()
obj1.read()
obj1.write()
obj2.read()
obj2.write()
obj3.read()
obj3.write()
'''得到記憶體或者硬碟物件之後 只要想讀取資料就呼叫read 想寫入資料就呼叫write 不需要考慮具體的物件是誰'''
面向物件之反射
# 什麼是反射
專業解釋:指程式可以訪問、檢測和修改本身狀態或者行為的一種能力
大白話:其實就是通過字串來操作物件的資料和功能
# 反射需要掌握的四個方法
hasattr():判斷物件是否含有字串對應的資料或者功能
getattr():根據字串獲取對應的變數名或者函式名
setattr():根據字串給物件設定鍵值對(名稱空間中的名字)
delattr():根據字串刪除物件對應的鍵值對(名稱空間中的名字)
# 哪些物件可以使用反射
python中一切都是物件,所以都可以使用反射來進行自省
類可以對類的共有屬性,方法(繫結方法,非繫結方法)進行反射
當前模組也可以進行反射 如:判定匯入的模組是否有某個方法,有的化就進行呼叫。
反射的實際應用
class Student(object):
school = '清華大學'
def get(self):
pass
# 編寫一個小程式 判斷Student名稱空間中是否含有使用者指定的名字 如果有則取出展示
# 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>
反射的好處
#反射的好處
實現可插拔機制
就是可先判定某個物件(模組物件,類物件,物件等)是否有某個屬性(是否插入),有則呼叫處理
沒有(拔出)則走另一條邏輯
這樣可以實現團隊開發中 實現預定好介面 就算呼叫的介面沒有具體完成,呼叫方也可以完成自己的邏輯
動態模組匯入
#動態模組匯入:a.import(模組名)函式 b. 使用importlib模組,使用importlib的import_module('模組名')
#動態匯入模組使用場景:
1. 動態引用模組的變數,可以利用反射,切換其引用的模組,並使用模組中的屬性。
2. 使用反射判斷是否有對應屬性,有則幹嘛,沒有則幹嘛。 兩種場景都是利用物件的反射來處理。核心就是利用字串來驅動不同的事件,比如匯入模組,呼叫函式等。
這是一種程式設計方法,設計模式的提現,凝聚了高內聚、鬆耦合的程式設計思想,不能簡單的用執行字串來代替。反射和exec()和eval()不同。
反射的案例
# 利用反射獲取配置檔案中的配置資訊
"""一切皆物件 檔案也是物件"""
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()