面向对象|面向对象 -- 类属性和类方法

实例对象

当使用类名()创建对象时,系统进行了两步操作:
  • 在内存中为对象分配存储空间
  • 调用初始化方法 __init__ 为对象进行初始化
当对象创建后,内存中就有了一个对象的实实在在的存在 —— 实例
  • 创建出来的对象叫做类的实例
  • 创建对象的动作叫做实例化
  • 对象的属性叫做实例属性
  • 对象调用的方法叫做实例方法
面向对象|面向对象 -- 类属性和类方法
文章图片
在程序执行时:
  • 每一个对象都有自己独立的内存空间,保存各自不同的属性
  • 可以通过self.访问自己的属性,调用自己的方法
  • 多个对象的方法,在内存中只有一份,调用方法时将当前对象的引用传递到方法内部
实例演示:工具箱
  • 定义一个工具类,每件工具都有自己的name属性
  • 定义一个类属性记录该类创建了多少个工具对象?
  • 设计类
面向对象|面向对象 -- 类属性和类方法
文章图片
  • 实现类
class Tool(object): count = 0 def __init__(self, name): self.name = name Tool.count += 1tool1 = Tool("锤子") tool2 = Tool("螺丝刀") tool3 = Tool("钳子")print("现在创建了 %d 个工具" % Tool.count)

类对象
  • 在程序运行时,类同样会被加载到内存中
  • 在Python中,类是一个特殊的对象 —— 类对象
  • 在程序运行时,类对象在内存中只有一份
  • 类对象拥有自己的属性自己的方法,分别称为类属性类方法
  • 通过类名.的方式访问类属性,调用类方法
面向对象|面向对象 -- 类属性和类方法
文章图片
类属性
  • 类属性是针对定义的属性,用于记录与这个类相关的特征
  • 使用类名.类属性名的方式访问
class 类名: 类变量名 = 值

【面向对象|面向对象 -- 类属性和类方法】注意:使用 对象.类属性 = 值 ,只会添加一个对象属性,而不会改变类属性的值
类方法
  • 类方法是针对定义的方法
  • 类方法内部可以直接访问类属性或者调用其他类方法
@classmethod def 类方法名(cls): pass

  • 类方法需要用修饰器@classmethod来标识
  • 类方法的第一个参数是cls,这只是习惯用法,也可以换成其他名字
  • cls用来指出类方法调用者,与self类似,不需要传递cls参数
  • 使用类名.类方法名()的形式调用类方法
  • 类方法内部,使用cls.属性名访问类属性cls.方法名调用其他类方法
实例演示:工具箱
工具类添加show_tool_count类方法,输出创建对象的个数
@classmethod def show_tool_count(cls): print("工具对象的总数 %d" % cls.count)

  • 类方法中只能访问类属性,调用其他的类方法
  • 实例方法中可以访问实例属性,也可访问类属性
  • 实例方法中可以调用其他的实例方法,也可调用其他的类方法
静态方法
在开发时,如果需要在类中封装一个方法,这个方法:
  • 不访问实例属性、实例方法,也不访问类属性、类方法
  • 这时可以把这个方法封装成一个静态方法
@staticmethod def 静态方法名(): pass

通过类名.静态方法名来调用静态方法
class Dog: @staticmethod def run(): print("狗在跑...")

实例演示:小霸王游戏机
需求:设计一个 Game 类
  • 属性:
    • 定义一个类属性top_score记录游戏的历史最高分
    • 定义一个实例属性player_name记录当前游戏的玩家姓名
  • 方法:
    • 定义静态方法show_help显示游戏帮助信息
    • 定义类方法show_top_score显示历史最高分
    • 定义实例方法start_game开始当前玩家的游戏
  • 设计类
面向对象|面向对象 -- 类属性和类方法
文章图片
  • 实现类
class Game(object): top_score = 0# 游戏最高分,类属性@staticmethod def show_help(): print("帮助信息:让僵尸走进房间")@classmethod def show_top_score(cls): print("游戏最高分是 %d" % cls.top_score)def __init__(self, player_name): self.player_name = player_namedef start_game(self): print("[%s] 开始游戏..." % self.player_name) Game.top_score = 999# 使用类名.修改历史最高分Game.show_help()# 1. 查看游戏帮助 Game.show_top_score()# 2. 查看游戏最高分 game = Game("小明")# 3. 创建游戏对象,开始游戏 game.start_game() Game.show_top_score()# 4. 游戏结束,查看游戏最高分

帮助信息:让僵尸走进房间 游戏最高分是 0 [小明] 开始游戏... 游戏最高分是 999

  • 实例方法内,可以访问实例属性实例方法类属性类方法
  • 类方法内,可以访问类属性类方法
  • 静态方法内,不能访问实例属性实例方法类属性类方法
单例设计模式
  • 设计模式:是前人工作的总结和提炼,通常,被人们广泛流传的设计模式都是针对某一特定问题的成熟解决方案。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性
  • 单例设计模式:在程序中只创建一个唯一的对象实例,每次执行类名()创建对象时,返回的都是同一个对象,内存地址是相同的
  • __new__方法
  • 使用类名()创建对象时,Python解释器首先调用__new__方法为对象分配存储空间
  • __new__方法是object类提供的内置静态方法,作用有两个,分配内存空间,返回对象引用
  • Python解释器获得对象的引用后,将引用作为第一个参数,传递给__init__方法
面向对象|面向对象 -- 类属性和类方法
文章图片
  • 单例模式的实现
  • 定义一个类属性,初始值设为None,用于记录单例对象的引用
  • 重写__new__方法
    如果类属性 is None,调用父类方法分配空间,并在类属性中记录结果
    否则返回类属性中记录的对象引用
面向对象|面向对象 -- 类属性和类方法
文章图片
class MusicPlayer(object): instance = None# 定义类属性记录单例对象引用def __new__(cls, *args, **kwargs): if cls.instance is None:# 判断类属性是否已经被赋值 cls.instance = super().__new__(cls)return cls.instance# 返回类属性的单例引用

  • 只执行一次初始化工作
重写__new__方法后,每次都得到唯一的对象引用,但是,初始化方法还是会被再次调用
解决方法:
  • 定义init_flag类属性,标记是否执行过初始化动作,初始值设为False
  • __init__方法中,判断init_flag,如果为False就执行初始化动作,并将init_flag设置为True
  • 这样,再次调用__init__方法时,初始化动作就不再执行了
class MusicPlayer(object): instance = None# 记录第一个被创建对象的引用 init_flag = False# 记录是否执行过初始化动作def __new__(cls, *args, **kwargs): if cls.instance is None:# 判断类属性是否为空 cls.instance = super().__new__(cls)# 调用__new__方法创建新对象 return cls.instance# 返回类属性保存的对象引用def __init__(self): if not MusicPlayer.init_flag: print("初始化音乐播放器") MusicPlayer.init_flag = Trueplayer1 = MusicPlayer() print(player1)player2 = MusicPlayer() print(player2)

初始化音乐播放器 <__main__.MusicPlayer object at 0x00000000029CA160> <__main__.MusicPlayer object at 0x00000000029CA160>




- end -

    推荐阅读