多态
多态是指:不同的对象调用相同的方法,将产生不同的结果。
多态是建立在 继承 和 重写 基础上的。
例:
- 在
Dog
类中封装方法game
- 普通狗只是简单的玩耍
- 定义
XiaoTianDog
继承自Dog
,并且重写game
方法- 哮天犬需要在天上玩耍
- 定义
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)
前:
后: