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)
        

关于小人射击项目的总结