类的结构
术语一 实例
使用面向对象开发,第1步是设计类
使用 类名()创建对象,创建对象的动作有两步:
在内存中为对象分配空间
调用初始化方法__init__为对象初始化
创建对象后,内存中就有了一个对象的实实在在的存在一实例

因此,通常也会把:
1.创建出来的对象叫做类的实例
2.创建对象的 动作 叫做 实例化
3.对象的属性 叫做 实例属性
4.对象调用的方法 叫做 实例方法
在程序执行时
1.对象各自拥有自己的 实例属性
2.调用对象方法,可以通过 self
访问自己的属性
调用自己的方法
结论
每一个对象,都有自己独立的内存空间,保存各自不同的属性
多个对象的方法,在内存中只有一份,在调用方法时,需要把对象的引用,传递到方法内部
类是一个特殊的对象
Python中一切皆对象:
class AAA:定义的类属于类对象
obj1=AAA()属于实例对象
在程序运行时,类同样会被加载到内存
在python中,类是一个特殊的对象一类对象
在程序运行时,类对象 在内存中 只有一份,使用一个类可以创建出很多个对象实例
除了封装实例 的属性和方法外,类对象可以拥有自己的属性和方法
1.类属性
2.类方法
通过类名.的方式可以访问类的属性或者调用类的方法

类属性和实例属性
概念和使用
类属性 就是给 类对象中定义的 属性
通常用来记录与这个类相关的特征
类属性不会用于记录具体对象的特征
示例需求
定义一个工具类
每件工具都有自己的name
需求--知道使用这个类,创建了多少个工具对象

"""
定义一个工具类
每件工具都有自己的name---->实例属性
需求--知道使用这个类,创建了多少个工具对象----》类属性
"""
class Tools(object):
count = 0 #类属性,用来描述这个类创建了多少个实例
def __init__(self,name):#初始化方法,是用来给对象(实例赋予属性值的)
self.name = name #实例属性
#通过这个类创建多少个对象,做一个计数count
#思路:每创建一个对象,就会调用一次__init__(self)方法,每调用一次__init__,就对count做一个+1的操作
Tools.count += 1
tool1=Tools("斧子") #创建实例
too12=Tools("匕首")
tool3 =Tools("工兵铲")
print(Tools.count)#输出通过类创建的对象数

类的获取机制
在python中属性的获取存在一个向上查找机制

因此,要访问类属性有两种方式:
1.类名.类属性
2.对象.类属性(不推荐)
注意
如果使用对象类.属性=值赋值语句,只会给对象添加一个属性,而不会影响到类属性的值
class Tools(object):
count = 0 #类属性,用来描述这个类创建了多少个实例
def __init__(self,name):#初始化方法,是用来给对象(实例赋予属性值的)
self.name = name #实例属性
#通过这个类创建多少个对象,做一个计数count
#思路:每创建一个对象,就会调用一次__init__(self)方法,每调用一次__init__,就对count做一个+1的操作
Tools.count += 1
tool1=Tools("斧子") #创建实例
too12=Tools("匕首")
tool3 =Tools("工兵铲")
print(Tools.count)#输出通过类创建的对象数
print(tool1.count)#注意:count属性,是Tools这个类的,但是可以被实例访问
print(id(tool1.count),id(Tools.count)) #2061306888496 2061306888496
tool1.count = 100 #该定义方式,只是给Tool1增加了一个属性count,增加了一个tool1.count的内存空间
# 而不会改变类属性的值
print(id(tool1.count),id(Tools.count)) #2061306888496 2061306888496
print(Tools.count) #---->3

类方法
类属性就是针对类对象定义的属性
使用 赋值语句 在class 关键字 下方可以定义类属性
类属性 用于记录与这个类相关的特征
类方法 就是记录与这个类相关的方法
在类方法内部可以直接访问类属性或者调用其他的类方法
语法如下
@classmethod
def method_name(cls):
pass
类方法需要用修饰器 @classmethod 来标识,告诉解释器这是一个类方法
类方法的 第一个参数 应该是cls
由 哪一个类 调用的方法,方法内的cls就是哪一个类的引用
这个参数和 实例方法的第一个参数和self类似
提示使用其他名称也可以,不过习惯使用cls
通过类名,调用类方法,调用方法时,不需要传递cls参数
在方法内部
可以通过 cls,访问类的属性
也可以通过cls调用其他的类方法
示例需求
定义一个工具类
每件工具都有自己的name
需求--------在类封装一个show_tool_count的类方法,输出使用当前这个类,创建的对象个数

class Tools(object):
count = 0
def __init__(self,name):
self.name = name
Tools.count += 1
#创建类方法
@classmethod
def show_tool_count(cls):
print(f"目前创建了{cls.count}个,工具实例了!")#类方法内部,访问了类属性
#print(self.name)#类方法内部,不能访问实例属性,也不能调用实例方法
tool1 = Tools("burpsuit")
tool2 = Tools("冰蝎")
Tools.show_tool_count()

在类方法内部,可以直接使用cls访问类属性或者调用类方法
静态方法
在开发时,如果需要在类中封装一个方法,这个方法:
既不需要访问实例属性或者调用实例方法
也不需要访问类属性或者调用类方法
这个时候,可以把这个方法封装成一个静态方法
语法如下
@staticmethod
def static_name():
pass
静态方法
静态方法 需要用修饰器 @staticmethod来标识,告诉解释器这是一个静态方法
通过类名,调用静态方法
class Dog:
def __init__(self):
self.name = "旺财"
@staticmethod
def run():
"""
定义了一个静态方法 静态方法不需要访问实例属性,也不需要调用实例方法
静态方法不需要访问类属性 也不需要访问调用类方法
:return:
"""
print("狗跑了...")
wangcai = Dog() #根据类创建实例
wangcai.run()

案例
- 设计一个Game类
- 属性
- 定义一个 类属性top_score记录游戏的 历史最高分
- 定义一个 实例属性player_game记录 当前游戏的玩家姓名
- 方法
- 静态方法show_help显示游戏帮助信息
- 类方法show_top_score显示历史最高分
- 实例方法start_game开始当前玩家的游戏
- 主程序步骤
- 查看帮助信息
- 查看历史最高分
- 创建游戏对象,开始游戏
import random
class Game(object):
top_score = 0 #类属性,用来记录历史最高分,默认为0
def __init__(self, name): #实例属性 ,通过__init__进行实例化
self.player_name = name
@staticmethod #静态方法
def show_help():
print("欢迎来到猜数字游戏。。。。")
@classmethod
def show_top_score(cls): #类方法 ,访问了 类属性
print(f"当前历史最高分是{cls.top_score}")
def start_game(self): #实例方法
print(f"{self.player_name}进入了游戏。。。。")
#开始游戏,产生了分数
Game.top_score = random.randint(50, 1000)
print(f"{self.player_name}在游戏中获得了{Game.top_score}分")
Game.show_help()
Game.show_top_score()
xiaoming = Game("小明。。。")
xiaoming.start_game()
Game.show_top_score()


案例小结
1.实例方法---方法内部需要访问 实例属性
实列方法内部口以使用类名. 访口类属性
2.类方法----方法内部只需要访问类属性或其他类方法
3.静态方法-方法内部,不需要访问 实例属性和 类属性
单例设计模式
设计模式
设计模式 是 前人工作的总结和提炼,通常,被人们广泛流传的设计都是针对某一特定问题 的成熟的方案
使用 设计模式 是为了可重用代码、让代码更容易被他人理解、保证代码可靠性
单例 设计模式
目的 — 让 类 创建的对象,在系统中只有唯一的一个实例
每一次执行 类名( )返回的对象,内存地址是相同的
单例设计模式的应用场景
音乐播放对象
回收站对象
打印机对象
……
__new__方法
使用 类名( )创建对象时,Python的解释器 首先会 调用__new__方法为对象分配空间
__new__是一个 由object基类提供的内置的静态方法,是要作用有两个:
在内存中为对象分配空间
返回对象的引用
Python的解释器获得对象的引用后,将引用作为第一个参数,传递给__init__方法
重写__new__方法的代码非常固定!
重写__new__方法 一定要return super().__new__(cls),用来返回对象的引用
否则Python的解释器得不到分配了空间的对象引用,就不会调用对象的初始化方法
注意:__new__是一个静态方法,在调用时需要 主动传递cls参数

定义MusicPlayer( )类,实例化对象
class MusicPlayer(object):
def __init__(self):
print("播放器初始化")
#创建播放器对象
play = MusicPlayer()
print(play)

重写new方法,验证new方法的作用和工作过程
class MusicPlayer(object):
#重写new方法
def __new__(cls):
#1.创建对象时,new方法会被自动调用
print("创建对象,分配空间")
#2.为对象分配空间
instance = super().__new__(cls)
#3.返回对象的引用
return instance
def __init__(self):
print("播放器初始化")
#创建播放器对象
play = MusicPlayer()
print(play)

Python中的单例
单例—让 类创建的对象,在系统中只有唯一的一个实例à保证引用的唯一性
定义一个 类属性,初始值是None,用于记录单例对象的引用
重写__new__方法
如果类属性is None,调用父类方法:__new__分配空间,并在类属性中记录结果
返回类属性中记录的对象引用

Python中的单例
class MusicPlayer(object):
pass
player1 = MusicPlayer()
print(player1)
player2 = MusicPlayer()
print(player2)

class MusicPlayer(object):
#记录第一次创建对象的引用
instance = None
#改进new方法
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)

只执行一次初始化工作
在每次使用 类名( )创建对象时,Python的解释器都会自动调用两个方法:
__new__分配空间
__init__对象初始化
在对__new__方法改造后,每次都会得到第一次被创建对象的引用
但是:初始化方法还会被再次调用
需求:让初始化动作只被执行一次
class MusicPlayer(object):
instance = None # instance设置为初始值None,如果 创建过对象,
#也就是 调用过new方法了,instance的值就不是None了
init_flag = False #定义一个初始值,用于标记__init__方法,没有被再次执行
def __new__(cls, *args, **kwargs): #重写__new__方法
if cls.instance is None: #如果 之前没有执行过new方法,则cls.instance is None
cls.instance = super().__new__(cls) #将 内存id赋值给cls.instance
return cls.instance #重写new方法后,将 内存id返回
def __init__(self):
if MusicPlayer.init_flag == True:
return
print("播放器初始化")
MusicPlayer.init_flag = True
player1 = MusicPlayer()
player2 = MusicPlayer()
print(id(player1)) #多个实例 2918561262816引用 是通过__new__返回的
print(id(player2))
