13-多态

多态

  • 面向对象特征

    • 封装:用类将属性和方法封装在一起,配合私有属性和私有方法使用

    • 继承:让子类可以继承父类的属性和方法,不包括私有的,子类可以扩展自己的属性和方法

      • 1.子类完全继承父类的所有属性和方法的情况,如:动物类,狗类,动物类作为父类,狗类作为子类,一般有从属关系
      • 2.如果父类中有部分属性和方法不继承,则需要将它们的公共的属性和方法抽取出来作为父类,如:狗类和猫类,将狗和猫的公共属性和方法提取出来作为父类
    • 多态(了解):在继承的基础上,而且需要用重写。要在父类中定义一个方法,让所有子类重写该方法,那么我们可以使用父类对象去指向不同的子类来调用同一个方法名,则有不同的状态或结果(animal看作是父类对象,Python没有真正的多态,这里将animal看作是父类的对象)

      • # 父类
        class Animal:
            def bark(self):
                pass
        
        # 子类
        class Dog(Animal):
            def bark(self):
                print('汪汪汪的叫')
        
        class Cat(Animal):
            def bark(self):
                print('喵喵喵的叫')
        
        class Wolf(Animal):
            def bark(self):
                print('嗷嗷嗷的叫')
        
        class Person:
            def __init__(self, name):
                self.name = name
            def beat_animal(self, animal):
                print(f'{self.name}正在殴打小动物')
                animal.bark()  # animal看作是父类对象,Python没有真正的多态,这里将animal看作是父类的对象
        
        # 对象
        dog = Dog()
        cat = Cat()
        wolf = Wolf()
        xiaoming = Person('黄晓明')
        xiaoming.beat_animal(dog)  # 调用小明,将动物子类的对象作为参数传递至对应的方法中
        
    • 抽象

获取对象信息

  • type():判断数据类型
  • isinstance():判断某个对象是否属于指定的类的对象,指定的类的对象可以是一个元组
  • dir():列出指定对象中所包含的所有的内容【成员变量/对象属性,成员方法】
    • dir() # 获得当前模块的属性列表
    • dir([ ]) # 查看列表的方法

类属性&类方法

  • 如果类属性的值是引用类型,则该类属性会被当前类创建的所有对象共享;如果该引用类型属性值是成员属性,则不会共享

    • class Person:
          # 类属性(类变量)
          name = '鹿晗'
          age = 10
          # 如果类属性的值是引用类型,则该类属性会被当前类创建的所有对象共享
          likes = ['关晓彤', '舒淇']
          
          def __init__(self, age):  # 在构造函数中初始化对象属性/成员变量
              # 对象属性(成员变量)
              self.age = age
              # 不会共享
              self.likes2 = ['关晓彤', '舒淇']  # 对象属性直接写死,不需要在构造函数中写出来
      
  • 对象属性如果直接给出初始值写死,则不需要在构造函数中写出来进行初始化

  • 对象.类属性/类.类属性/对象.对象属性

  • 针对相同名称的类属性和对象属性,对象调用会优先调用对象属性

  • 可以对对象属性和类属性进行修改

单例模式

  • 让类只能创建出一个对象

  • 设计模式:一种解决问题的方式

  • 设计模式:23种设计模式,比如:单例模式,工厂模式,代理模式,装饰器模式,适配器模式

  • 使用new方法

    • class Person:
          # 构造函数:用来对属性初始化
          def __init__(self):
              print('__init__')
      
          # 类属性
          instance = None
          
          # new方法:用来创建对象
          @classmethod
          def __new__(cls, *args, **kwargs):  # 自动先调用new, 重写new
              print('__new__')  # __new__
              if not cls.instance:
                  print('---创建新对象---')
                  cls.instance = super(Person, cls).__new__(*args, **kwargs)  # 新建对象,相当于修改类属性.Person是父类,父类对应cls(特殊形参)
                  # cls.instance = object.__new__(cls)  # object可以换成super()
              return cls.instance
      
      # 对象
      p = Person()
      p2 = Person()
      print(p == p2)  # True
      
      • 初始化一个类属性并使其值为None
      • 利用类方法重写创建对象的方法:new方法
      • 隐式调用类的new方法来新建对象,并赋值给初始化的类属性,相当于修改类属性
  • 使用装饰器

    • # 装饰器
      def outer(cls):
          instance = None
          print('instance初始化为None')
          def inner(*args, **kwargs):  # 通用装饰器
              nonlocal instance
              if not instance:
                  print('---创建了新的对象---')
                  instance = cls(*args, **kwargs)  # cls相当于Person类, 新建对象
              print('---返回对象---')
              return instance
          return inner
      
      @outer  # 加上装饰器的一瞬间调用一次instance = None,后面不会再调用
      class Person:
          pass
      
      p1 = Person()
      p2 = Person()
      print(p1 is p2)  # True
      
      • 加上装饰器的一瞬间调用一次在外函数初始化值为None的变量,后面构建对象时,直接用的是内函数
  • 使用类方法

    • # 使用类方法
      class Singleton:
          instance = None
      
          @classmethod
          def get_instance(cls):
              if not cls.instance:
                  cls.instance = cls()  # 相当于修改类的属性,cls()相当于Singleton()
              return cls.instance
      
      s1 = Singleton.get_instance()  # 通过调用类方法新建对象
      s2 = Singleton.get_instance()
      print(s1 is s2)  # True
      
      • 在创建对象时调用类方法进行新建

生产者消费者模式

  • 如果一个线程的运行是根据另一个线程的结果,那么需要使用生产者和消费者模式

  • import time
    from threading import Thread
    import queue
    
    q = queue.Queue()
    
    def make():
        num = 1
        while True:
            print(f'做了{num}个包子')
            q.put(num)
            num += 1
            time.sleep(1)
    
    def eat():
        while True:
            num = q.get()
            print(f'吃了{num}个包子')
    
    if __name__ == '__main__':
        t1 = Thread(target=make)
        t2 = Thread(target=eat)
        t1.start()
        t2.start()
        t1.join()
        t2.join()
    

类方法和静态方法

  • class Person:
        # 构造方法
        def __init__(self, name):
            self.name = name
    
        # 成员方法(常用)
        def sleep(self):
            print('睡觉', self)
    
        # 私有方法
        def __run(self):
            print('跑步')
    
        # 类方法
        # 1.可以使用类和对象调用,但是建议使用类来调用,可以节省内存
        # 2.可以使用类属性和其他类方法,但是不能使用对象属性和成员方法和私有方法
        # 3.一般用在功能比较独立、和类中其他属性和方法无关的情况下
        # cls: class
        @classmethod
        def eat(cls):
            print('吃饭:', cls==Person)  # 在类方法中不能使用对象属性和成员方法,因为没有self
    
        # 静态方法
        # 1.可以使用类和对象调用,但是建议使用类来调用,可以节省内存
        # 2.不可以使用对象属性和成员方法和私有方法,一般也不用类属性和类方法
        # 3.就是一个非常普通的函数,只是写在类里面
        @staticmethod
        def sing():
            print('唱歌')
    
    # 对象
    p = Person('蔡徐坤')
    Person.eat()  # 类.类方法  # 吃饭: True(类是不占内存的)
    p.eat()  # 对象.类方法 # 吃饭: True
    Person.sleep(p)  # 睡觉,类.成员方法(不推荐,尽量不这么用)
    p.sleep()  # 睡觉,对象.成员方法
    
    Person.sing()  # 唱歌,类.静态方法,推荐
    p.sing()  # 唱歌,对象.静态方法
    
    • 类方法@classmethod
      • 1.可以使用类和对象调用,但是建议使用类来调用,可以节省内存
      • 2.可以使用类属性和其他类方法,但是不能使用对象属性和成员方法和私有方法
      • 3.一般用在功能比较独立、和类中其他属性和方法无关的情况下
    • 静态方法@staticmethod
      • 1.可以使用类和对象调用,但是建议使用类来调用,可以节省内存
      • 2.不可以使用对象属性和成员方法和私有方法,一般也不用类属性和类方法
      • 3.就是一个非常普通的函数,只是写在类里面
    • 类.类方法/对象.类方法/类.成员方法(不推荐,需要传入对象)/对象.成员方法/类.静态方法/对象.静态方法

动态添加属性和方法

  • 动态添加的属性:只存在于当前的对象中

  • 动态添加方法

    • 需要传入对象

    • class Person:
          age = 20
          def __init__(self, name):
              self.name = name
      
      # 对象
      p = Person('成龙')
      print(p.name)  # '成龙'
      
      # 动态添加属性:只存在于当前的对象中
      p.age = 10
      print(p.name, p.age, Person.age)  # 成龙 10 20
      # print(Person('李小龙').age)  # 报错
      
      # 动态添加方法:了解
      def sleep(self):
      	print(self.name, '在睡觉')
          
      p.sleep2 = sleep
      p.sleep2(p)  # 调用, 成龙 在睡觉
      
      from types import MethodType
      
      def sleep(self):
          print(self.name, '在睡觉')
      
      p.sleep3 = MethodType(sleep, p)
      p.sleep3()  # 调用, 成龙 在睡觉
      
      # 类属性赋值要使用类.类属性的方式赋值
      # 每个对象的对象属性是独立的内存
      

特殊属性

  • 类.__name__ # 类
  • 对象.__class__ # __main__.类
  • 对象.__dict__ # 快速获取对象的所有属性及其属性值
  • 对象.__module__ # __main__
  • 类.__module__ # __main__
  • 类.__bases__ # 父类
  • 类.__mro__ # 查看某个类的继承顺序
  • print(__name__) # __main__

运算符重载

  • class Girl:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        # 加法
        def __add__(self, other):  # 重写运算符方法
            return self.age + other.age
    
    # 对象
    p1 = Girl('杨超越', 22)
    p2 = Girl('关晓彤', 20)
    print(p1.age + p2.age) # 42
    print(p1 + p2)  # 42
    
    • 让类可以使用运算符
    • 相当于重写内部的运算符方法
原文地址:https://www.cnblogs.com/lotuslaw/p/14008863.html