多态

多态是指:不同的对象调用相同的方法,将产生不同的结果。
多态是建立在 继承重写 基础上的。

例:

  1. Dog 类中封装方法 game
    • 普通狗只是简单的玩耍
  2. 定义 XiaoTianDog 继承自 Dog,并且重写 game 方法
    • 哮天犬需要在天上玩耍
  3. 定义 Person 类,并且封装一个 和狗玩 的方法
    • 在方法内部,直接让 狗对象 调用 game 方法
class Dog(object):

    def __init__(self, name):
        self.name = name

    def game(self):
        print("%s 蹦蹦跳跳地玩耍..." % self.name)


class XiaoTianDog(Dog):

    def game(self):
        print("%s 飞到天上去玩耍..." % self.name)


class Person(object):

    def __init__(self, name):
        self.name = name

    def game_with_dog(self, dog):

        print("%s 和 %s 快乐的玩耍..." % (self.name, dog.name))
        dog.game()


wangcai = XiaoTianDog("飞天旺财")
snb = Dog("史努比")
xiaoming = Person("小明")

xiaoming.game_with_dog(wangcai)  
xiaoming.game_with_dog(snb)  

类的深入介绍

使用类创建出的对象就叫 类的实例。对象的属性和方法分别叫做 实例属性实例方法 。创建对象的动作叫做 实例化

类属性

在 Python 中,一切皆对象,类也是一个特殊的对象,所以也可以给类定义方法和属性。通常是定义一些与这个类相关的特征。

class Tool(object):

    # 记录创建对象的总数
    count = 0

    def __init__(self, name):

        self.name = name
        Tool.count += 1


tool1 = Tool("斧头")
tool2 = Tool("榔头")
tool3 = Tool("铁锹")

print("现在创建了 %d 个工具" % Tool.count)

输出:现在创建了 3 个工具

属性的获取机制

因此,访问类属性有两种方式:

  • 类名.类属性
  • 对象.类属性

推荐使用类名来调用类属性,专职专用,避免产生混淆。

类方法

类方法 就是针对 类对象 定义的方法。

@classmethod
def 类方法名(cls):
    pass
  • 类方法需要用 修饰器 @classmethod 来标识,告诉解释器这是一个类方法
  • 类方法的第一个参数应是 cls,这个参数和 实例方法 的第一个参数是 self 类似,由哪一个类调用的方法,方法内的 cls 就是哪一个类的引用
  • 通过 类名. 调用类方法时,不需要传递 cls 参数
  • 在方法内部:
    • 可以通过 cls. 访问类的属性
    • 也可以通过 cls. 调用其他的类方法
class Tool(object):

    count = 0

    @classmethod
    def show_tool_count(cls):
        print("工具对象的总数 %d" % cls.count)

    def __init__(self, name):

        self.name = name
        Tool.count += 1


tool1 = Tool("斧头")
tool2 = Tool("榔头")
tool3 = Tool("铁锹")

Tool.show_tool_count()

输出:工具对象的总数 3

静态方法

在类中封装一个方法,如果这个方法既 不需要 访问 实例属性 / 方法,也 不需要 访问 类属性 / 方法,这时就可以考虑封装一个静态方法。

@staticmethod
def 静态方法名():
    pass

静态方法不需要传递 self 参数。

class Dog(object):
    
    @staticmethod
    def run():
        print("跑")

综合案例

设计一个 Game 类:

  • 属性:
    • 类属性 top_score 记录游戏的 历史最高分
    • 实例属性 player_name 记录 当前游戏的玩家姓名
  • 方法:
    • 静态方法 show_help 显示游戏帮助信息
    • 类方法 show_top_score 显示历史最高分
    • 实例方法 start_game 开始当前玩家的游戏
class Game(object):

    top_score = 0

    def __init__(self, player_name):
    self.player_name = player_name

    @staticmethod
    def show_help():
        print("帮助信息")
        
    @classmethod
    def show_top_score(cls):
        print("游戏最高分是 %d" % cls.top_score)

    def start_game(self):
        print("[%s] 开始游戏了..." % self.player_name)
        Game.top_score = 999


Game.show_help()
Game.show_top_score()

game = Game("小明")
game.start_game()

Game.show_top_score()

小结

  • 实例方法 —— 方法内部需要访问 实例属性
  • 类方法 —— 方法内部 需要访问 类属性
  • 静态方法 —— 方法内部不需要访问 实例属性 / 类属性

例:如果方法内部,既需要访问实例属性,又需要访问类属性,应该定义一个实例方法。


单例设计模式

设计模式 是前人工作的总结和提炼,通常都是针对某一特定问题的成熟的解决方案。

单例 设计模式:让 创建的对象,在系统中只有 唯一 的一个实例,这样每一次执行 类名(),返回的对象内存地址是相同的。

所以,所谓单例,即个实

如:

  • 音乐播放 对象:每次只放一首歌
  • 回收站 对象:操作系统的回收站只有一个
  • 打印机 对象:打印机只有一台

__new__ 方法

使用 类名() 创建对象时,Python 解释器首先会调用内置方法 __new__ 为对象 分配空间,并 返回引用。解释器获得对象的 引用 后,将引用作为第一个参数(self)传递给 __init__ 方法。

重写 __new__ 方法时一定要 return super().__new__(cls),否则 Python 解释器得不到分配了空间的 对象引用,就不会调用对象的初始化方法 __init__

class MusicPlayer(object):

    def __new__(cls, *args, **kwargs):
        print("创建对象,分配空间")
        return super().__new__(cls)

    def __init__(self):
        print("音乐播放器初始化")


player = MusicPlayer()
print(player)

单例设计模式的实现

一般情况下,使用类创建的对象是不同的对象:

class MusicPlayer(object):

    pass


player1 = MusicPlayer()
print(player1)

player2 = MusicPlayer()
print(player2)

可以看到它们的地址是不同的,所以是不同的对象。

使用 __new__ 方法,根据单例的概念,可以实现单例设计模式:

class MusicPlayer(object):

    # 定义类属性 instance 记录单例对象引用
    instance = None

    def __new__(cls, *args, **kwargs):

        # 首次创建对象
        if cls.instance is None:
            cls.instance = super().__new__(cls)

        return cls.instance


player1 = MusicPlayer()
print(player1)

player2 = MusicPlayer()
print(player2)

可以看到两次创建的对象是相同的。

然而细想可以发现,使用类名创建几次对象,初始化方法 __init__ 就调用了几次。

在开发时,有可能有只执行一次初始化的需求。为此,可以定义一个变量来记录是否执行过初始化动作。

class MusicPlayer(object):


    instance = None
    init_flag = False

    def __new__(cls, *args, **kwargs):


        if cls.instance is None:
            cls.instance = super().__new__(cls)

        return cls.instance

    def __init__(self):

        if not MusicPlayer.init_flag:
            print("初始化音乐播放器")
            MusicPlayer.init_flag = True



player1 = MusicPlayer()
print(player1)

player2 = MusicPlayer()
print(player2)

前:

后: