Python 基础学习笔记 04

Python基础系列
Python 基础学习笔记 01
Python 基础学习笔记 02
Python 基础学习笔记 03
Python 基础学习笔记 04
Python 基础学习笔记 05

5.1. 面向过程与面向对象

  • 面向过程

    1. 把完成某一个需求的所有步骤从头到尾逐步实现
    2. 根据开发需求,将某些功能独立的代码封装成一个又一个函数
    3. 最后完成的代码,就是顺序的调用不同的函数

    特点

    1. 注重步骤与过程,不注重职责分工
    2. 如果需求复杂,代码会变得很复杂
    3. 开发复杂项目,没有固定的套路,开发难度很大
  • 面向对象

    1. 在完成某一个需求前,首先确定职责–要做的事情
    2. 根据职责确定不同的对象,在对象内部封装不同的方法
    3. 最后完成的代码,就是顺序的让不同的对象调用不同的方法

    特点

    1. 注重对象和职责,不同的对象承担不同的职责
    2. 更加适应应对复杂的需求变化,是专门应对复杂项目开发,提供的固定套路
    3. 需求在面向过程的基础上,再学习一些面向对象的语法

5.2. 类和对象的概念

    • 类是对一群具有相同特征和行为的事务的一个统称,是抽象的,不能直接使用
      • 特征被称为属性
      • 行为被称为方法
  • 对象

    • 对象是由类创建出来的一个具体存在,可以直接使用
    • 由哪一个类创建出来的对象,就拥有在哪一个类中定义的属性和方法
  • 类与对象的关系

    • 类是模板,对象是根据这个模板创建出来的,应该先有类,再有对象
    • 类只有一个,而对象可以有很多个
    • 不同的对象之间属性可能会各不相同
    • 类中定义了什么属性和方法,对象中就有什么属性和方法,不可能多,也不可能少
  • 类的设计

    • 三个要素:

      1. 类名 这类事务的名字,满足大驼峰命名法

        大驼峰命名法:每一个单词的首字母大写,单词与单词之间没有下划线

      2. 属性 这类事务具有什么样的特征

      3. 方法 这类事务具有什么样的行为

  • 类名的确定

    • 名词提炼法分析整个业务流程,出现的名词,通常就是找到的类
  • 属性和方法的确定

    • 对象的特征描述,通常可以定义成属性
    • 对象具有的行为,通常可以定义成方法

5.3. 面向对象基础语法

5.3.1. dir内置函数
  • 使用内置函数dir传入标识符/数据,可以查看对象内的所有属性及方法
  • __方法名__格式的方法是python提供的内置方法/属性
    1. __new__:创建对象时,会被自动调用
    2. __init__:对象被初始化时,会被自动调用
    3. __del__:对象被从内存中销毁前,会被自动调用
    4. __str__:返回对象的描述信息,print函数输出使用
5.3.2. 定义简单的类
  • 定义只包含方法的类

    class 类名:
      	def 方法1(self,参数列表):
    				pass
      
      	def 方法2(self,参数列表):
     				pass
    
  • 创建对象

    • 当一个类定义完成后,要使用这个类创建对象,语法格式:

      对象变量 = 类名()
      
  • 第一个面向对象的程序

    class Cat:
    
        def eat(self):
            print("小猫爱吃鱼")
    
        def drink(self):
            print("小猫要喝水")
    
    
    # 创建猫对象
    tom = Cat()
    tom.eat()
    tom.drink()
    
  • 引用概念

    • tom变量引用了新建的猫对象
    • 使用print输出对象变量,默认情况下,是能够输出这个变量引用的对象是由哪一个类创建的对象,以及在内存中的地址(十六进制表示)
5.3.3. 方法中的self参数
  • 在类的外部,通过变量名.访问对象的属性和方法
  • 在类封装的方法中,通过self.访问对象的属性和方法
5.3.4. 初始化方法
  • 当使用类名()创建对象时,会自动执行以下操作:

    1. 为对象在内存中分配空间—创建对象
    2. 为对象的属性设置初始值—初始化方法(init)__init__专门用来定义类中的属性的方法
    • 代码实例:

      class Cat:
          # 初始化方法,创建对象的时候会自动调用
          def __init__(self,new_name):
              # self.属性名=属性的初始值
              self.name = new_name
      
          def eat(self):
              print("%s爱吃鱼" % self.name)
      
      # Tom是一个全局变量
      tom = Cat("Tom")
      tom.eat()
      print(tom.name)
      
  • __del__方法

    • 当使用类名()创建对象时,为对象分配完空间后,自动调用__init__方法

    • 当一个对象被从内存中销毁前,会自动调用__del__方法

    • 生命周期

      • 一个对象从调用类名()创建,生命周期开始
      • 一个对象的__del__方法一旦被调用,生命周期结束
      • 在对象的生命周期内,可以访问对象属性,或者让对象调用方法
    • 代码实例:

      class Cat:
          def __init__(self,new_name):
              self.name = new_name
              print("%s来了" % self.name)
      
          def __del__(self):
              print("%s我去了"%self.name)
              
      tom = Cat("Tom")
      print(tom.name)
      del tom
      
  • __str__方法

    • 使用print输出对象变量,默认情况下,会输出这个变量引用的对象是由哪一个类创建的对象,以及在内存中的地址(十六进制表示)

    • 如果在开发中,希望使用print输出对象变量时,能够打印自定义的内容,就可以利用__str__这个内置方法

    • 代码实例:

      class Cat:
          def __init__(self,new_name):
              self.name = new_name
              print("%s来了" % self.name)
      
          def __del__(self):
              print("%s我去了"%self.name)
      
          def __str__(self):
              return "我是小猫[%s]" % self.name
      
      
      tom = Cat("Tom")
      print(tom)
      

5.4. 面向对象封装

  • 封装

    • 封装是面向对象编程的一大特点
    • 面向对象编程的第一步:将属性和方法封装到一个抽象的类中
    • 外界使用类创建对象,然后让对象调用方法
    • 对象方法的细节都被封装在类的内部
  • 代码实例:(小明爱跑步)

    class Person:
        def __init__(self,name,weight):
            self.name = name
            self.weight = weight
    
        def __str__(self):
            return "我的名字叫%s 体重%.2f" % (self.name,self.weight)
    
        def run(self):
            print("%s爱跑步,跑步锻炼身体" % self.name)
    
        def eat(self):
            print("%s是吃货,吃完这顿再减肥" % self.name)
    
    
    xiaoming = Person("小明",75.0)
    xiaoming.run()
    xiaoming.eat()
    print(xiaoming)
    
  • 代码实例:(小明爱跑步,方法内部可以直接访问对象的属性)

    class Person:
        def __init__(self,name,weight):
            self.name = name
            self.weight = weight
    
        def __str__(self):
            return "我的名字叫%s 体重%.2f" % (self.name,self.weight)
    
        def run(self):
            print("%s爱跑步,跑步锻炼身体" % self.name)
    
        def eat(self):
            print("%s是吃货,吃完这顿再减肥" % self.name)
            self.weight += 1
    
    
    xiaoming = Person("小明",75.0)
    xiaoming.run()
    xiaoming.eat()
    print(xiaoming)
    
  • 代码实例:(摆放家具)

    • 需求:

请添加图片描述

  • 家具类:

    # 创建家具类
    class HouseItem:
      
        def __init__(self,name,area):
            self.name = name
            self.area = area
    
            
        def __str__(self):
            return "[%s]占地%.2f" % (self.name,self.area)
    
  • 房间类:

    class House:
      
        def __init__(self,house_type,area):
            self.house_type = house_type
            self.area = area
            self.free_area = area
            self.item_list = []
    
            
        def __str__(self):
            # Python能够自动的将一对括号内部的代码连接在一起
            return ("户型:%s\n总面积%.2f[剩余:%.2f]\n家具:%s"
                    % (self.house_type,self.area,
                       self.free_area,self.item_list))
    
          
        def add_item(self,item):
            print("要添加%s" % item)
            # 1.判断家具的面积是否超过剩余面积,如果超过,提示不能添加这件家具
            if item.area > self.free_area:
                print("%s的面积太大了,无法添加" % item.name)
                return
            # 2.将家具的名称追加到家具名称列表中
            self.item_list.append(item.name)
            # 3.用房子的剩余面积-家具面积
            self.free_area -= item.area
    
  • 具体步骤:

    # 1.创建家具对象
    bed = HouseItem("席梦思",4)
    chest = HouseItem("衣柜",2)
    table = HouseItem("餐桌",1.5)
    # 打印家具信息
    print(bed)
    print(chest)
    print(table)
    
    # 2.创建房子对象
    my_home = House("两室一厅",60)
    
    # 添加家具
    my_home.add_item(bed)
    my_home.add_item(chest)
    my_home.add_item(table)
    
    print(my_home)
    
  • 代码实例:(士兵突击)

    • 需求:

请添加图片描述

  • 枪类:

    class Gun:
        def __init__(self,model):
            # 枪的型号
            self.model = model
            # 子弹的数量
            self.bullet_count = 0
    
        def add_bullet(self,count):
            self.bullet_count += count
    
        def shoot(self):
            # 1.判断子弹数量
            if self.bullet_count <= 0 :
                print("[%s]没有子弹了" % self.model)
                return
            # 2.发射子弹,-1
            self.bullet_count -= 1
            # 3.提示发射信息
            print("[%s]突突突 [%d]" % (self.model,self.bullet_count))
    
  • 士兵类:

    class Soldier:
        def __init__(self,name):
            # 1.姓名
            self.name = name
            # 2.枪 - 新兵没有枪
            self.gun = None
    
        def fire(self):
            # 1.判断士兵是否有枪
            # if self.gun == None:
            # 身份运算符用于比较两个对象的内存地址是否一致--是否是对同一个对象的引用
            if self.gun is None:
                print("[%s] 还没有枪" % self.name)
                return
              
            # 2.高喊口号
            print("冲啊 [%s]" % self.name)
    
            # 3.让枪装填子弹
            # 对象调用方法
            self.gun.add_bullet(50)
    
            # 4.让子弹发射
            self.gun.shoot()
    
  • 具体步骤:(一个类的属性可以是另外一个类创建的对象)

    # 1.创建枪对象
    ak47 = Gun("AK47")
    
    # 2.创建许三多
    xusanduo = Soldier("许三多")
    
    # 一个对象的属性可以是另外一个类创建的对象
    # 士兵类的属性gun是枪类创建的对象ak47
    xusanduo.gun = ak47
    
    xusanduo.fire()
    
  • 身份运算符

    • 身份运算符用于比较两个对象的内存地址是否一致—是否是对同一个对象的引用

    • 在python中,针对None比较时,建议使用is判断

    • is与==区别:

      is用于判断两个变量引用对象是否为同一个

      ==用于判断引用变量的值是否相等

      a = [1,2,3]
      b = [1,2,3]
      
      print(b is a)
      print(b == a)
      

5.5. 私有属性和私有方法

  • 属性和方法不希望在类外使用时,可以定义为私有属性和私有方法。在定义属性和方法时,在属性名或者方法名前增加两个下划线,定义的就是私有属性或方法

    # 定义私有属性__age和私有方法__secret
    class Woman:
        def __init__(self,name):
            self.name = name
            self.__age = 18
    
        def __secret(self):
            print("%s的年龄是%d" % (self.name,self.__age))
    
    xiaofang  = Woman("小芳")
    
    # 私有属性,在外界不能被直接访问
    # print(xiaofang.__age)
    
    # 私有方法,在外界不能被直接访问
    # xiaofang.__secret()
    
  • 伪私有属性和私有方法

    • 在给属性、方法命名时,实际是对名称做了一些特殊处理,使得外界无法访问到
    • 处理方式:在名称前面加上_类名=>_类名__名称
    # 私有属性,在外界不能被直接访问
    print(xiaofang._Woman__age)
    
    # 私有方法,在外界不能被直接访问
    xiaofang._Woman__secret()
    

5.6. 继承

5.6.1. 面向对象三大特性
  • 封装:根据职责将属性和方法封装到一个抽象的类中
  • 继承:实现代码的重用。相同的代码不需要重复的编写
  • 多态:不同的对象调用相同的方法,产生不同的执行结果,增加代码的灵活度
# 不使用继承开发
class Animal:
    def eat(self):
        print("吃")

    def drink(self):
        print("喝")

    def run(self):
        print("跑")

    def sleep(self):
        print("睡")

class Dog:
    def eat(self):
        print("吃")

    def drink(self):
        print("喝")

    def run(self):
        print("跑")

    def sleep(self):
        print("睡")

    def bark(self):
        print("汪汪叫")


wangcai = Dog()
wangcai.sleep()
wangcai.run()
wangcai.drink()
wangcai.eat()
wangcai.bark()

#----------------------------------------------------------------------------------------

# 使用继承开发
class Animal:
    def eat(self):
        print("吃---")

    def drink(self):
        print("喝---")

    def run(self):
        print("跑---")

    def sleep(self):
        print("睡---")


class Dog(Animal):
    def bark(self):
        print("汪汪叫")


wangcai = Dog()
wangcai.sleep()
wangcai.run()
wangcai.drink()
wangcai.eat()
wangcai.bark()
5.6.2. 单继承
  • 继承的概念:子类拥有父类的所有方法和属性

  • 语法

    class 类名(父类名):
    	pass
    
    • 子类继承自父类,可以直接享受父类中已经封装好的方法,不需要再次开发
    • 子类应该根据职责,封装子类特有的属性和方法
  • 继承的传递

    class Animal:
        def eat(self):
            print("吃---")
        def drink(self):
            print("喝---")
        def run(self):
            print("跑---")
        def sleep(self):
            print("睡---")
    
    
    class Dog(Animal):
        def bark(self):
            print("汪汪叫")
    
    
    class XiaoTianQuan(Dog):
        def fly(self):
            print("我会飞")
    
    
    xtq = XiaoTianQuan()
    xtq.run()
    xtq.bark()
    xtq.fly()
    
  • 重写父类的方法

    1. 覆盖父类的方法

      • 如果在开发中,父类的方法实现与子类完全不同,就可以使用覆盖的方式,在子类中重新编写父类的方法实现。具体的实现方式,就相当于在子类中定义了一个和父类同名的方法并且实现。重写之后,在运行时,只会调用子类中重写的方法,而不会再调用父类封装的方法

        class Animal:
            def eat(self):
                print("吃---")
            def drink(self):
                print("喝---")
            def run(self):
                print("跑---")
            def sleep(self):
                print("睡---")
        
        
        class Dog(Animal):
            def bark(self):
                print("汪汪叫")
        
        
        class XiaoTianQuan(Dog):
            def fly(self):
                print("我会飞")
            def bark(self):
                print("叫的和神一样")
        
        # 创建对象
        xtq = XiaoTianQuan()
        # 如果子类中,重写了父类的方法,程序调用时子类重写的方法
        xtq.bark()
        
    2. 对父类方法进行扩展

      • 如果在开发中,子类的方法实现中包含父类的方法实现,父类原本封装的方法实现是子类方法的一部分,就可以使用扩展的方式。

        1. 在子类中重写父类的方法
        2. 在需要的位置使用super().父类方法来调用父类方法的执行
        3. 代码其他的位置针对子类的需求,编写子类特有的代码实现
        class Animal:
            def eat(self):
                print("吃---")
            def drink(self):
                print("喝---")
            def run(self):
                print("跑---")
            def sleep(self):
                print("睡---")
        
        
        class Dog(Animal):
            def bark(self):
                print("汪汪叫")
        
        
        class XiaoTianQuan(Dog):
            def fly(self):
                print("我会飞")
            def bark(self):
                # 1.针对子类特有的需求,编写代码
                print("神一样的叫唤")
                # 2.使用super(),调用父类中的封装方法
                # super().bark()
                # 使用父类名称调用父类的方法
                Dog.bark(self)
                # 3.增加其他代码
        
        
        xtq = XiaoTianQuan()
        # 如果子类中,重写了父类的方法,程序调用子类重写的方法
        xtq.bark()
        
  • 关于super

    • 在python中super是一个特殊的类
    • super()就是使用super类创建出来的对象
    • 最常使用的场景就是在重写父类方法时,调用在父类中封装的方法实现
  • 父类中的私有属性和私有方法

    • 子类对象不能在自己的方法内部,直接访问父类的私有属性或私有方法

      class A:
          def __init__(self):
              self.num1 = 100
              self.__num2 = 200
      
          def __test(self):
              print("私有方法%d %d" % (self.num1,self.__num2))
      
      
      class B(A):
          def demo(self):
              # 1.访问父类的私有属性,无法访问
              # print("访问父类的私有属性 %d" % self.__num2)
              # 2.访问父类的私有方法,无法访问
              self.__test()
      
      # 创建一个子类对象
      b = B()
      print(b)
      
      # 在外界不能访问类的私有属性和私有方法
      b.demo()
      
    • 子类对象可以通过父类的公有方法间接访问到私有属性或私有方法

      class A:
          def __init__(self):
              self.num1 = 100
              self.__num2 = 200
      
          def __test(self):
              print("私有方法%d %d" % (self.num1,self.__num2))
      
          def test(self):
              self.__test()
              print("公有方法")
      
      
      class B(A):
          def demo(self):
              # 1.访问父类的私有属性,无法访问
              # print("访问父类的私有属性 %d" % self.__num2)
              # 2.访问父类的私有方法,无法访问
              self.test()
      
      # 创建一个子类对象
      b = B()
      # 在外界不能访问类的私有属性和私有方法
      b.demo()
      
      
5.6.3. 多继承
  • 概念:子类可以拥有多个父类,并且具有所有父类的属性和方法

  • 语法

    class 子类名(父类名1,父类名2……)
    	pass
    
    class A:
        def test(self):
            print("A -- test方法")
    
    
    class B:
        def demo(self):
            print("B -- demo方法")
    
    
    class C(A,B):
        """多继承可以让子类对象,同时具有多个父类的属性和方法"""
        pass
    
    
    # 创建子类对象
    c = C()
    
    c.test()
    c.demo()
    
  • 多继承的使用注意事项

    • 应该避免不同的父类中有同名的方法

    • 可以使用__mro__查看方法的搜索顺序

      class A:
          def test(self):
              print("A -- test方法")
              
          def demo(self):
              print("A -- demo方法")
      
      
      class B:
          def demo(self):
              print("B -- demo方法")
              
          def test(self):
              print("B -- test方法")
      
      
      class C(A,B):
          pass
      
      
      # 创建子类对象
      c = C()
      
      c.test()
      c.demo()
      
      # 确定c类对象调用方法的顺序
      print(C.__mro__)
      
      • 在搜索时,是按照__mro__的输出结果从左至右的顺序查找的
      • 如果在当前类中找到方法,就直接执行,不再搜索
      • 如果没有找到,就查找下一个类中是否有对应的方法,如果找到,就直接执行,不再搜索
      • 如果找到最后一个类,还没有找到方法,程序报错

5.7. 多态

  • 多态 不同的子类对象调用相同的父类方法,产生不同的执行结果

    • 多态可以增加代码的灵活度

    • 以继承和重写父类方法为前提

    • 是调用方法的技巧,不会影响到类的内部设计

      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()
      
      
      # 1.创建一个狗对象
      wangcai = XiaoTianDog("飞天神犬")
      # 2.创建一个小明对象
      xiaoming = Person("小明")
      
      # 3.让小明调用和狗玩的方法
      xiaoming.game_with_dog(wangcai)
      

5.8. 类属性和类方法

5.8.1. 类的结构
  • 实例

    1. 使用面向对象开发,第一步是设计类

    2. 使用类名()创建对象,创建对象的动作有两步

      1)在内存中为对象分配空间

      2)调用初始化方法__init__为对象初始化

    3. 对象创建后,内存就有了一个对象的实实在在的存在—实例

    通常会把:

    1. 创建出来的对象叫做类的实例
    2. 创建对象的动作叫做实例化
    3. 对象的属性叫做实例属性
    4. 对象的调用的方法叫做实例方法

    在程序执行时:

    1. 对象各自拥有自己的实例属性
    2. 调用方法时,可以通过self.调用自己的属性和方法

    结论:

    1. 每一个对象都有自己独立的内存空间,保存各自不同的属性
    2. 多个对象的方法,在内存中只有一份,在调用方法时,需要把对象的引用传递到方法内部
  • 类是一个特殊的对象

    • 在程序运行时,类同样会被加载到内存
    • 在python中,类是一个特殊的对象—类对象
    • 在程序运行时,类对象在内存中只有一份,使用一个类可以创建出很多个对象实例
    • 除了封装实例的属性和方法外,类对象还可以拥有自己的属性和方法:类属性和类方法
    • 通过类名.的方式可以访问类的属性或者调用类的方法
5.8.2. 类属性和实例属性
  • 概念和使用:类属性就是给类对象中定义的属性,通常用来记录与这个类相关的特征,类属性不会用于记录具体对象的特征

    class Tool(object):
        # 使用赋值语句定义类属性,记录所有工具对象的数量
        count = 0
    
        def __init__(self,name):
            self.name = name
            # 让类属性的值 +1
            Tool.count += 1
    
    
    # 1.创建工具对象
    tool1 = Tool("斧头")
    tool2 = Tool("榔头")
    tool3 = Tool("水桶")
    
    # 2.输出工具对象的总数
    print(Tool.count)
    
  • 属性的获取机制:属性的获取存在一个向上获取机制,首先在对象内部查找对象属性,没有找到就会向上寻找类属性。访问类属性的两种方式:1)类名.类属性 2)对象.类属性

    # 获取属性有一个向上获取机制
    print("工具对象总数 %d" % tool3.count)
    

    注意:如果使用对象.类属性 = 值 赋值语句,只会给对象添加一个属性,而不会影响到类属性的值

    class Tool(object):
        count = 0
    
        def __init__(self,name):
            self.name = name
            # 让类属性的值+1
            Tool.count += 1
    
    # 1.创建工具对象
    tool1 = Tool("斧头")
    tool2 = Tool("榔头")
    tool3 = Tool("水桶")
    
    # 新增一个类属性的值,不会更改类属性的值
    tool3.count = 99
    print("工具对象总数 %d" % tool3.count)
    print(Tool.count)
    
5.8.3. 类方法和静态方法
  • 类属性就是针对类对象定义的属性

    • 使用赋值语句在class关键字下方可以定义类属性
    • 类属性用于记录与这个类相关的特征
  • 类方法就是针对类对象定义的方法

    • 在类方法内部可以直接访问类属性或者调用其他的类方法
  • 语法:

    @classmethod
    def 类方法名(cls):
    	pass
    
  • 类方法需要用修饰器@classmethod来标识,告诉解释器这是一个类方法

  • 类方法的第一个参数应是cls

    • 由哪一个类调用的方法,方法内的cls就是哪一个类的引用
    • 这个参数和实例方法的第一个参数是self类似
    • 提示:使用其他名称也可以,不过习惯使用cls
  • 通过类名.调用类方法,调用方法时,不需要传递cls参数

  • 在方法内部

    • 可以通过cls.访问类属性
    • 也可以通过cls.调用其他的类方法
    class Tool(object):
      	# 创建类属性
        count = 0
    
        @classmethod
        def show_tool_count(cls):
          	# 使用cls.的方式访问类属性
            print("工具对象的数量%d" % cls.count)
    
        def __init__(self,name):
            self.name = name
            Tool.count += 1
    
    
    # 创建工具对象
    tool1 = Tool("斧头")
    tool2 = Tool("榔头")
    
    Tool.show_tool_count()
    
  • 静态方法

    • 如果需要一个类中封装一个方法,这个方法:

      • 即不需要访问实例属性或者调用实例方法
      • 也不需要访问类属性或者调用类方法
    • 语法:

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

      • 静态方法需要用修饰器@staticmethod来标识,告诉解释器这是一个静态方法
      • 通过类名. 调用静态方法,不需要创建对象
      class Dog(object):
          @staticmethod
          def run():
              # 不访问实例属性/类属性
              print("小狗要跑")
      
      # 通过类名.调用静态方法 - 不需要创建对象
      Dog.run()
      
5.8.4. 方法综合案例

请添加图片描述

  • 综合案例:

    class Game(object):
        # 定义类属性
        top_score = 0 
    
        def __init__(self,play_name):
          	# 定义实例属性
            self.player_name = play_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)
    
    
    # 1.查看游戏的帮助信息(类名.静态方法)
    Game.show_help()
    
    # 2.查看游戏最高分(类名.类方法)
    Game.show_top_score()
    
    # 3.创建游戏对象(对象.实例方法)
    game = Game("小明")
    game.start_game()
    

5.9. 单例

  • 单例设计模式

    • 设计模式
      • 设计模式是前人工作的总结和提炼,通常,被人们广泛流传的设计模式都是针对某一特定问题的成熟的解决方案
      • 使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性
    • 单例设计模式
      • 目的:让类创建的对象,在系统中只有唯一的一个实例
      • 每一次执行类名()返回的对象,内存地址是相同的
    • 应用场景
      • 音乐播放对象
      • 回收站对象
    • 打印机对象
  • __new__方法

    • 使用类名()创建对象时,Python的解释器首先会调用__new__方法为对象分配空间
    • __new__是一个由object基类提供的内置的静态方法,主要作用有两个:
      1. 在内存中为对象分配空间
      2. 返回对象的引用
    • python解释器获得对象的引用后,将引用作为第一个参数,传递给__init__方法
    • 重写__new__方法一定要return suer().__new__(cls),否则python的解释器得不到分配了空间的对象引用,就不会调用对象的初始化方法
    • 注意:__new__是一个静态方法,在调用时需要主动传递cls参数
  class MusicPlayer(object):
  
      def __new__(cls, *args, **kwargs):
          # 1.创建对象时,new方法会被自动调用
          print("创建对象,分配空间")
          # 2.为对象分配空间
          instance = super().__new__(cls)
          # 3.返回对象的引用
          return instance
  
      def __init__(self):
          print("播放器初始化")
  
  # 创建播放器对象
  player = MusicPlayer()
  
  print(player)
  • Python中的单例

    • 单例:让类创建的对象,在系统中只有唯一的一个实例

      1. 定义一个类属性,初始值是None,用于记录单例对象的引用

      2. 重写__new__方法

      3. 如果类属性is None,调用父类方法分配空间,并在类属性中记录结果

      4. 返回类属性中记录的对象引用

        class MusicPalyer(object):
          # 定义类属性记录单例对象引用
          instance = None
          
          def __new__(cls,*args,**kwargs):
            # 1.判断类属性是否已经被赋值
            if cls.instance is None:
              # 2.调用父类的方法,为第一个对象分配空间
              cls.instance = super().__new__(cls)
              
            # 3.返回类属性的单例引用
            return cls.instance
          
        # 以下两个对象只会产生一个地址的引用
        palyer1 = MusicPlayer()
        print(palyer1)
        player2 = MusicPlayer()
        print(player2)
        
  • 只执行一次初始化工作

    • 在每次使用类名()创建对象时,python的解释器都会自动调用两个方法:

      • __new__分配空间
      • __init__对象初始化
      • 在上一小节对__new__方法改造之后,每次都会得到第一次被创建对象的引用
      • 但是:初始化方法还会被再次调用
    • 需求

      • 让初始化方法只被执行一次
        1. 定义一个类属性init_flag标记是否执行过初始化动作,初始值为False
        2. __init__方法中,判断init_flag,如果为Flase就执行初始化动作
        3. 然后将init_flag设置为True
      1. 再次自动调用__init__方法时,初始化动作就不会被再次执行了
      class MusicPlayer(object):
          # 记录第一个被创建对象的引用
          instance = None
      
          # 记录是否执行过初始化动作
          init_flag = False
      
          def __new__(cls, *args, **kwargs):
              # 1.判断类属性是否为空对象
              if cls.instance is None:
                  # 2.调用父类的方法,为第一个对象分配空间
                  cls.instance = super().__new__(cls)
              # 3.返回类属性的对象引用
              return cls.instance
      
          def __init__(self):
              # 1.判断是否执行过初始化动作
              if MusicPlayer.init_flag:
                  return
              # 2.如果没有执行过,在执行初始化动作
              print("初始化播放器")
              # 3.修改类属性的标记
              MusicPlayer.init_flag = True
      
      
      palyer1 = MusicPlayer()
      print(palyer1)
      player2 = MusicPlayer()
      print(player2)
      
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值