封裝,多型,反射
阿新 • • 發佈:2022-04-09
繼承下的派生實際應用
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) """ 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方法 """
面向物件-------封裝
簡介
封裝
1.封裝是面向物件程式設計的一大特點
2.面向物件程式設計的第一步 將屬性和方法封裝到一個抽象的類中
3.外界使用類建立物件,然後讓物件呼叫方法
4.物件方法的細節都被封裝在類的內部
說簡單的就是隱藏物件的屬性和實現細節,僅對外提供公共訪問方式。
基礎入門 —> setter、getter
''' 封裝就是指隱藏物件中一些不希望外部所訪問到的屬性或方法,即為了保證安全 如何封裝,或者說如何隱藏屬性或方法? 在定義類時,將屬性或方法名修改為外部並不知道的名稱,如下定義類 ''' class dog: def __init__(self,name,age): self.name = name self.age = age def hello(self): print(f'大家好,我是{self.name}') #例項化一個物件 d = dog('哈士奇',3) d.hello() #直接可以修改類中的屬性,不安全 d.name = '金毛' d.hello() 執行結果 大家好,我是哈士奇 大家好,我是金毛 ''' 那麼我們可以這樣定義,即內部使用hidden_name來定義屬性,外部就不能直接通過name來修改訪問類中的屬性了 ''' class dog: def __init__(self,name,age): self.new_name = name self.new_age = age def hello(self): print(f'大家好,我是{self.new_name}') #例項化一個物件 d = dog('哈士奇',3) d.hello() #外部不可以修改類中的屬性 d.name = '金毛' d.hello() 執行結果 大家好,我是哈士奇 大家好,我是哈士奇 ''' 這樣外部雖然不能通過name來修改屬性值,但是還是可以通過new_name來修改屬性值,當然我們是站在上帝的視角來看待這個問題,對於例項化類的物件而言,其並不是完全知道類中的屬性與方法 那麼,此時物件如何知道和修改屬性的值呢? 很簡單,在類中定義用於修改和獲取屬性的方法 set_name、get_name 需要提供一個getter和setter方法使外部可以訪問到屬性 getter 獲取物件中的指定屬性(get_屬性名) setter 用來設定物件的指定屬性(set_屬性名) ''' 且看如下程式碼 class dog: def __init__(self,name,age): self.new_name = name self.new_age = age def hello(self): print(f'大家好,我是{self.new_name}') #用於例項化物件設定修改類屬性的方法 def set_name(self,name): self.new_name = name #用於例項化物件獲取得到類屬性的方法 def get_name(self): return self.new_name #例項化一個物件 d = dog('哈士奇',3) d.hello() #外部使用get_name()方法來修改類中的屬性 d.get_name() #外部使用set_name()方法來修改類中的屬性 d.set_name('金毛') d.hello() ''' 這樣做有什麼好處呢? 使用封裝,確實增加了類的定義的複雜程度,但是它也確保了資料的安全性 ''' 1.隱藏了屬性名,使呼叫者無法隨意的修改物件中的屬性 2.增加了getter和setter方法,很好的控制屬性是否是隻讀的 如果希望屬性是隻讀的,則可以直接去掉setter方法 如果希望屬性不能被外部訪問,則可以直接去掉getter方法 3.使用setter方法設定屬性,可以增加資料的驗證,確保資料的值是正確的 在方法中判斷年齡等屬性的數值是否滿足正常範圍 >0 4.使用getter方法獲取屬性,使用setter方法設定屬性;可以在讀取屬性和修改屬性的同時做一些其他的 處理
進階使用 —> 私有屬性
以上只是根據在面向物件中對封裝的簡單概述,但是在真實環境中用的不是以上的這些方式,用的是Python 中自動的處理封裝的屬性,如下所示 可以為物件的屬性使用雙下劃線開頭,__xxx(屬性名) 雙下劃線開頭的屬性,是物件的隱藏屬性,隱藏屬性只能在類的內部訪問,無法通過物件訪問 其實隱藏屬性只不過是Python自動為屬性改了一個名字 實際上是將名字修改為了,_類名__屬性名 比如 __name -> _Person__name,這樣還是可以訪問的 防君子不防小人(並不是絕對不能訪問,如果一定要訪問還是可以通過物件名._類名__屬性名來訪問) class dog: def __init__(self,name,age): self.__name = name self.__age = age def hello(self): print(f'大家好,我是{self.__name},我{self.__age}歲了') #用於例項化物件設定修改類屬性的方法 def set_name(self,name): self.__name = name #用於例項化物件獲取得到類屬性的方法 def get_name(self): return self.__name #例項化一個物件 d = dog('哈士奇',3) d.hello() print(d._dog__name) # 其實可以通過如下方式修改私有屬性的值,但是不會這麼做,因為外部是不可以修改私有屬性的值的 d._dog__name = '金毛' d._dog__age = '4' d.hello() 執行結果 大家好,我是哈士奇,我3歲了 哈士奇 大家好,我是金毛,我4歲了
強化加強 —> property
property裝飾器,用來將一個get方法,轉換為物件的屬性
新增為property裝飾器以後,我們就可以像呼叫屬性一樣使用get方法
使用屬性的方式去呼叫方法,看上去是對屬性賦值,但是本質上是呼叫方法的
使用property裝飾的方法,必須和屬性名是一樣的
設定setter前必須設定getter
class dog:
def __init__(self,name,age):
self._name = name
self._age = age
def hello(self):
print(f'大家好,我是{self._name},我{self._age}歲了')
@property
def name(self):
print('get方法執行了~~~')
return self._name
# setter方法的裝飾器:@屬性名.setter
@name.setter
def name(self,name):
print('set方法執行了~~~')
self._name = name
@property
def age(self):
print('get方法執行了~~~')
return self._age
# setter方法的裝飾器:@屬性名.setter
@age.setter
def age(self,age):
print('set方法執行了~~~')
self._age = age
#例項化一個物件
d = dog('哈士奇',3)
d.hello()
print('--'*30)
# 呼叫set方法,修改name的值;注意看上去是修改屬性名的方式,其實是呼叫的了方法,注意看輸出
d.name = '德牧'
d.hello()
print('--'*30)
d.age = 4
d.hello()
print('--'*30)
print(d.name)
# 執行結果
# 大家好,我是哈士奇,我3歲了
# ------------------------------------------------------------
# set方法執行了~~~
# 大家好,我是德牧,我3歲了
# ------------------------------------------------------------
# set方法執行了~~~
# 大家好,我是德牧,我4歲了
# ------------------------------------------------------------
# get方法執行了~~~
# 德牧
面向物件-------多型
多型顧名思義多種狀態,在python中,不同的物件呼叫同一個介面,表現出不同的狀態,稱為多型。
要實現多型有兩個前提:
1.繼承:多型必須發生在父類與子類之間
2.重寫:子類重寫父類方法
例:
class Animal():
def who(self):
print("I am an Animal")
class Duck(Animal):
def who(self):
print("I am a duck")
class Dog(Animal):
def who(self):
print("I am a dog")
class Cat(Animal):
def who(self):
print("I am a cat")
if __name__ == "__main__":
duck=Duck()
dog=Dog()
cat=Cat()
duck.who()
dog.who()
cat.who()
'''多型的使用增強了程式的靈活性和可擴充套件性'''
反射
hasattr():判斷物件是否含有字串對應的資料或者功能
getattr():根據字串獲取對應的變數名或者函式名
setattr():根據字串給物件設定鍵值對(名稱空間中的名字)
delattr():根據字串刪除物件對應的鍵值對(名稱空間中的名字)
class Hero(object):
def __init__(self,name,blood):
self.name = name
self.blood = blood
@property
def get_name(self):
return self.name
@get_name.setter
def set_name(self,name):
self.name = name
hero = Hero('卡莎',1000)
1.hasattr(object,name)
判斷object中有沒有name字串對應的方法和屬性,name可以是函式也可以是引數,還可以是變數,返回結
果為True、False
print(hasattr(hero,'name')) # True
2.getattr(object,name,default=None)
獲取name屬性,如果沒有會報錯,但是新增default屬性後,沒有name會直接列印default值
print(getattr(hero,'get_name')) # 卡莎
print(getattr(hero,'f1')) # <bound method Hero.f1 of <__main__.Hero object at 0x01E6D1C0>>
3.setattr(object,屬性名,屬性值)
返回值是None
設定屬性物件的屬性,還可以直接修改當前物件的屬性
print(setattr(hero,'name','女警')) # None
print(hero.name) # 女警
4.delattr(object,屬性名)
刪除物件屬性
print(hero.__dict__) # {'name': '卡莎', 'blood': 1000}
delattr(hero,'name')
print(hero.__dict__) # {'blood': 1000}