day24:多態&魔術方法__new__&單態模式
- 2020 年 8 月 5 日
- 筆記
- Python全棧31期-筆記
目錄
多態
什麼是多態?
不同的子類對象,調用相同的父類方法,產生不同的執行結果
多態的關鍵字:繼承和改寫
下面的例子完美展示了python的多態:
# 定義Soldier類,讓空軍陸軍海軍繼承這個類 class Soldier(): def attack(self): pass def back(self): pass # 陸軍 class Army(Soldier): def attack(self): print("[陸軍]搏擊,ufc,無限制格鬥,太極,八卦,占星,製作八卦符") def back(self): print("[陸軍]白天晨跑10公里,也行800百公里") # 海軍 class Navy(Soldier): def attack(self): print("[海軍]潛泳水下30個小時,手捧魚雷,親自送到敵人的老撾,炸掉敵人的碉堡") def back(self): print("[海軍]每小時在海底夜行800公里,游的比鯊魚還快") # 空軍 class AirForce(Soldier): def attack(self): print("[空軍]空中奪導彈,手撕飛機,在空中打飛機,精準彈幕") def back(self): print("[空軍]高中跳傘,落地成盒") # 實例化陸軍對象 army_obj = Army() # 實例化海軍對象 navy_obj = Navy() # 實例化空軍對象 af_obj = AirForce() lst = [army_obj,navy_obj,af_obj] # 對象列表 strvar = """ 1.所有兵種開始攻擊訓練 2.所有兵種開始撤退訓練 3.空軍練習攻擊,其他兵種練習撤退 """ print(strvar) num = input("將軍請下令,選擇訓練的種類") for i in lst: if num == "1": i.attack() elif num == "2": i.back() elif num == "3": if isinstance(i,AirForce): i.attack() else: i.back() else: print("將軍~ 風太大 我聽不見~") break
__new__魔術方法
關於魔術方法__new__
1.觸發時機:實例化類生成對象的時候觸發(觸發時機在__init__之前)
2.功能:控制對象的創建過程
3.參數:至少一個cls接受當前的類,其他根據情況決定
4.返回值:通常返回對象或None
基本語法
# (1)基本語法 class MyClass2(): pty = 100 obj2= MyClass2() class MyClass(): def __new__(cls): print(cls) # 類.方法(自定義類) => 藉助父類object創建MyClass這個類的對象 obj = object.__new__(cls) # (1) 藉助父類object創建自己類的一個對象 return obj # (2) 返回其他類的對象 return obj2 # (3) 不返回任何對象 return None obj = MyClass() print(obj)
__new__ 觸發時機快於構造方法
__new__ 用來創建對象
__init__ 用來初始化對象
先創建對象,才能在初始化對象,所以__new__快於__init__
class Boat(): def __new__(cls): print(2) return object.__new__(cls) def __init__(self): print(1) obj = Boat() # 因為__new__比__init__先執行,所以會先打印2後打印1.
__new__ 和 __init__ 參數一一對應
單個參數的情況
class Boat(): def __new__(cls,name): return object.__new__(cls) def __init__(self,name): self.name = name obj = Boat("友誼的小船說裂開就裂開") print(obj.name)
多個參數的情況
# 當__init__參數很多的時候,在__new__方法使用*args和**kwargs收集所有的參數!! class Boat(): def __new__(cls,*args,**kwargs): return object.__new__(cls) def __init__(self,name,color,shangpai): self.name = name self.color = color self.shangpai = shangpai obj = Boat("泰坦尼克號","屎綠色","京A66688") print(obj.name) print(obj.color) print(obj.shangpai)
關於__new__的注意點
如果返回的對象不是自己本類中的對象,不會觸發本類的構造方法
"""如果返回的對象不是自己本類中的對象,不會觸發本類的構造方法""" class MyClass(): pty = 200 obj = MyClass() class Boat(): def __new__(cls,*args,**kwargs): return object.__new__(cls) # 返回的是自己類的對象,會觸發__init__方法 # return obj # 返回的不是自己的對象,所以不會觸發下面的__init__方法 # return None # 沒有返回對象,不可能觸發__init__方法 def __init__(self): print("構造方法被觸發") obj = Boat() print(obj)
單態模式
什麼是單態模式?
一個類,無論實例化多少個對象,都有且只有一個對象
單態模式的應用場景
優點:節省內存空間,提升執行效率
針對於不要額外對該對象添加成員的場景(比如:mysql增刪改查)
基本語法
class SingleTon(): __obj = None def __new__(cls): if cls.__obj is None: # 把創建出來的對象賦值給私有成員__obj cls.__obj = object.__new__(cls) return cls.__obj obj1 = SingleTon() obj2 = SingleTon() obj3 = SingleTon() print(obj1,obj2,obj3) """ 第一次實例化對象時候, 創建一個對象賦值給cls.__obj,返回 cls.__obj 第二次實例化對象時候, 判定cls.__obj is None: 不成立, 直接返回上一次創建好的那一個對象 第三次實例化對象時候, 判定cls.__obj is None: 不成立, 直接返回上一次創建好的那一個對象 """
單態模式+構造方法
class SingleTon(): __obj = None def __new__(cls,*args,**kwargs): if cls.__obj is None: cls.__obj = object.__new__(cls) return cls.__obj def __init__(self,name): self.name = name obj1 = SingleTon("A") obj2 = SingleTon("B") print(obj1.name) print(obj2.name) # 打印出來的是兩個B,原因如下 ''' obj1 = SingleTon(宋雲傑) self.name = "A" obj2 = SingleTon(戈隆) self.name = "B" self 代表的是本對象 第一次實例化對象時候,創建一個對象,賦值name為A 第二次實例化對象時候,因為 cls.__obj is None不滿足,返回上一個創建好的對象 為上一個對象的name這個屬性賦值 為B obj1 和 obj2 所指代的對象是同一個對象 obj1.name => B obj2.name => B 其實有些相當於是B把A覆蓋了,因為B和A指向同一個對象 '''
連貫操作
連貫操作,通過在類中的__init__方法中傳入obj對象,進而實現對象.對象.對象.屬性的這種連貫操作
通過.不停的調用下一個對象的操作就是連貫操作
1.小試牛刀
class MyClass1(): pth1 = 10 class MyClass2(): def __init__(self,obj): self.obj = obj obj = MyClass1() obj2 = MyClass2(obj) # 對象.對象.屬性 # obj2.obj = obj # obj.pth1 = 10 print(obj2.obj.pth1) # 10
2.升級版
class MyClass1(): pty1 = 101 def func1(self): print("我是func1函數") class MyClass2(): def __init__(self,obj): self.pty2 = obj def func2(self): print("我是func2函數") class MyClass3(): pty3 = 103 def __init__(self,obj): self.pty3 = obj def func2(self): print("我是func3函數") obj1 = MyClass1() obj2 = MyClass2(obj1) obj3 = MyClass3(obj2) # 使用obj3調用func1方法? # 對象.pty3 => obj2 | obj2.pty2 => obj1 | obj1.func1() obj3.pty3.pty2.func1() print(obj3.pty3.pty2.pty1) obj3.pty3.func2()
連貫操作小項目->小人射擊
小人射擊的項目需求
小人射擊項目的文件結構
文件夾下:
package包中:
小人射擊項目的代碼實現
main.py(主函數)
# ### 小人射擊 """面向對象的核心思想: 把對象當做程序中的一個最小單元,讓對象操作一切""" """ 彈夾: 屬性: 子彈數量 bulletcount 方法: 無 槍 : 屬性: 彈夾 方法: 射擊 shoot 人 : 屬性: 槍 方法: (1) 射擊 (2) 換子彈 """ from package.bulletbox import BulletBox from package.gun import Gun from package.person import Person # 先創建彈夾 danjia = BulletBox(20) # 在創建一桿槍 ak47 = Gun(danjia) # 在創造一個人 songyunjie = Person(ak47,"宋雲傑") # 小人射擊 if __name__ == "__main__": # 射擊 songyunjie.fire(15) # 填充 songyunjie.fillcount(10) # 在射擊 songyunjie.fire(150)
bulletbox.py(彈夾)
# ### 彈夾類 class BulletBox(): def __init__(self,bulletcount): self.bulletcount = bulletcount
gun.py(槍)
# ### 槍類 class Gun(): def __init__(self,bulletbox): # 存放的是彈夾對象 self.bulletbox = bulletbox def shoot(self,shootcount): if self.bulletbox.bulletcount < shootcount: print("對不起,請先填充子彈~") else: # 剩餘的子彈 = 總的子彈數量 - 射擊的數量 self.bulletbox.bulletcount -= shootcount print("突" * shootcount , "你射出了{}發,還剩下{}發".format(shootcount,self.bulletbox.bulletcount))
person.py(人)
# ### 人類 class Person(): def __init__(self,gun,name): self.gun = gun self.name = "宋雲傑" # 填充子彈 def fillcount(self,fillnum): # 往彈夾裏面賽子彈 # self.槍對象 -> 彈夾對象 -> 彈夾數量屬性 self.gun.bulletbox.bulletcount += fillnum # 射擊 def fire(self,num): print("{}此刻正在野外射擊~".format(self.name)) # 槍對象.shoot self.gun.shoot(num)
關於小人射擊項目的總結