day24:多態&魔術方法__new__&單態模式

目錄

1.多態

2.__new__魔術方法

  2.1 關於魔術方法__new__

  2.2 基本語法

  2.3 __new__ 觸發時機快於構造方法

  2.4 __new__ 和 __init__ 參數一一對應

  2.5 關於__new__的注意點

3.單態模式

4.連貫操作

5.小人射擊項目

多態

什麼是多態?

不同的子類對象,調用相同的父類方法,產生不同的執行結果

多態的關鍵字:繼承和改寫

下面的例子完美展示了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)
        

關於小人射擊項目的總結