面向对象02 继承

Posted lianzibing

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面向对象02 继承相关的知识,希望对你有一定的参考价值。

  • 继承 :解决代码的重复

    #继承语法 class 子类名(父类名):pass
    class A:
        pass
    class B(A):
        pass
    
    # A 父类 基类 超类
    # B子类 派生类
    • 子类可以使用父类中的 : 方法和静态变量

      class Animal:
          def __init__(self,name):
              self.name = name
          def eat(self):
              print('%s is eating'%self.name)
          def drink(self):
              print('%s is drinking'%self.name)
          def sleep(self):
              print('%s is sleeping'%self.name)
      class Cat(Animal):
          def climb_tree(self):
              print('%s is climbing'%self.name)
      
      class Dog(Animal):
          def house_keep(self):
              print('%s house keeping'%self.name)
      
      小白 = Cat('小白')
          # 先开辟空间,空间里有一个类指针-->指向Cat
          # 调用init,对象在自己的空间中找init没找到,到Cat类中找init也没找到,
          # 找父类Animal中的init
      小白.eat()#小白 is eating
      小白.climb_tree()#小白 is climbing
      小黑 = Dog('小黑')
      小黑.eat()#小黑 is eating
    • 当子类和父类的方法重名的时候,我们只使用子类的方法,而不会去调用父类的方法了

      class Animal:
          def __init__(self,name):
              self.name = name
          def eat(self):
              print('%s is eating'%self.name)
          def drink(self):
              print('%s is drinking'%self.name)
          def sleep(self):
              print('%s is sleeping'%self.name)
      
      class Cat(Animal):
          def eat(self):
              print('%s吃猫粮'%self.name)
      
          def climb_tree(self):
              print('%s is climbing'%self.name)
      
      小白 = Cat('小白')
      小白.eat()#小白吃猫粮
    • 子类想要调用父类的方法的同时还想执行自己的同名方法

      • 在子类的方法中调用父类的方法 :父类名.方法名(self)

        class Animal:
            def __init__(self,name,food):
                self.name = name
                self.food = food
                self.blood = 100
                self.waise = 100
            def eat(self):
                print('%s is eating %s'%(self.name,self.food))
            def drink(self):
                print('%s is drinking'%self.name)
            def sleep(self):
                print('%s is sleeping'%self.name)
        
        class Cat(Animal):
            def eat(self):
                self.blood += 100
                Animal.eat(self)
            def climb_tree(self):
                print('%s is climbing'%self.name)
                self.drink()
        
        class Dog(Animal):
            def eat(self):
                self.waise += 100
                Animal.eat(self)
            def house_keep(self):
                print('%s is keeping the house'%self.name)
        小白 = Cat('小白','猫粮')
        小黑 = Dog('小黑','狗粮')
        小白.eat()#小白 is eating 猫粮
        小黑.eat()#小黑 is eating 狗粮
        print(小白.__dict__)#'name': '小白', 'food': '猫粮', 'blood': 200, 'waise': 100
        print(小黑.__dict__)#'name': '小黑', 'food': '狗粮', 'blood': 100, 'waise': 200
        
        #父类和子类方法的选择:
        # 子类的对象,如果去调用方法
        # 永远优先调用自己的
            # 如果自己有 用自己的
            # 自己没有 用父类的
            # 如果自己有 还想用父类的 : 直接在子类方法中调父类的方法 父类名.方法名(self)
  • 单继承

    # 单继承
    # 调子类的 : 子类自己有的时候
    # 调父类的 : 子类自己没有的时候
    # 调子类和父类的 :子类父类都有,在子类中调用父类的
    class D:
        def func(self):
            print('in D')
    class C(D):pass
    class A(C):
        def func(self):
            print('in A')
    class B(A):pass
    B().func()
  • 多继承

    • 有一些语言不支持多继承 比如 java
    • python语言的特点 : 可以在面向对象中支持多继承
    # 多继承
    # 一个类有多个父类,在调用父类方法的时候,按照继承顺序,先继承的就先寻找
    class D:
        def func(self):
            print('in D')
    class C(D):pass
    class A(C):
        def func(self):
            print('in A')
    class B(A):pass
    B().func()
  • object类 类祖宗

    • 所有在python3当中的类都是继承object类的

      class A:pass
      print(A.__bases__)#(<class 'object'>,)
      class C:pass
      class B(A,C):pass
      print(B.__bases__)#(<class '__main__.A'>, <class '__main__.C'>)只能找到父类 不能找到父类的父类
  • 绑定方法和普通的函数
    from types import FunctionType,MethodType
    # FunctionType : 函数
    # MethodType : 方法
    class A:
        def func(self):
            print('in func')
    
    print(A.func)  # 函数 #<function A.func at 0x000002841C479488>
    a = A()
    print(a.func)  # 方法 #<bound method A.func of <__main__.A object at 0x000002841C308240>>
    print(isinstance(a.func,FunctionType))#False
    print(isinstance(a.func,MethodType))#True
    print(isinstance(A.func,FunctionType))#True
    print(isinstance(A.func,MethodType))#False
    
    #类.func是函数
    #对象.func是方法
  • pickel 可以序列化对象

    • 前提py文件里有对象的类

      class Course:
          def __init__(self,name,period,price):
              self.name = name
              self.period = period
              self.price = price
      
      python = Course('python','6 moneth',21800)
      linux = Course('linux','5 moneth',19800)
      go = Course('go','4 moneth',12800)
      import  pickle
      with open('pickle_file','ab') as f:
          pickle.dump(linux,f)
          pickle.dump(go,f)
      with open('pickle_file','rb') as f:
          while True:
              try:
                  obj = pickle.load(f)
                  print(obj.name,obj.period)
              except EOFError:
                  break
      

      继承进阶

类 的继承

多继承

  • 只要继承object类就是新式类
  • 不继承object类的都是经典类

在python3中 所有的类都继承object类,都是新式类

在python2中 不继承object的类都是经典类,继承object类的就是新式类了

经典类:在py3中不存是,在py2中不主动继承object的类

在py2中
class A:pass   #经典类
class B(object):pass#新式类

在py3中
class A:  #pass #新式类
class B(object):pass #新式类

在单继承方面(无论是新式类还是经典类都是一样的)

class A:
    def func(self):pass
class B(A):
    def func(self):pass
class C(B):
    def func(self):pass
class D(c):
    def func(self):pass
d = D()

寻找某一个方法的顺序:D->C->B->A
越往父类走,是深度

多继承

class A:
    def func(self):
        print('A')
class B(A):
    def func(self):
        print('B')
class C(A):
    def func(Self):
        print(C)
class D(B,C):
    def func(self):
        print('B')
print(D.mro()) #只在新式类中有,经典类没有
d = D()
d.func()
在走到一个点,下一个点既可以深度走,也可以从广度走的时候,总是先走广度,再走深度,广度优先
在经典类总,都是深度优先,总是在一条路走不通之后再换一条路,走过的点不会再走了

C3算法

算法内容:

  1. 如果是单继承 那么总是按照从子类->父类的顺序来计算查找顺序
  2. 如果是多继承 需要按照自己本类,父类1的继承顺序,父类2的继承顺序
  3. merge的规则:如果一个类出现在从左到右所有顺序的最左侧,并没有在其他位置出现,那么先提出了作为继承顺序中的一个 或 一个类出现在从左到右顺序的最左侧,并没有在其他顺序出现那么先提出了作为继承顺序中的第一个
  4. 如果从左到右第一个顺序中的第一个类出现在后面且不是第一个,那么不能提取,顺序向后继续找其他顺序中父类上述条件的类
  • super — 是按照mro顺序来寻找当前类的下一个父类的 (与mro方法的关系)
  1. 在python3中不需要传参数,自动就帮我们寻找当前类的mro顺序的下一个类中的同名方法

  2. 在python2中的新式类中,需要我们主动传递参数 super(子类的名字和子类的对象.函数名(),这样才能够帮我们调用到这个子类的mre顺序的下一个类中的方法

    在python2中的经典类中,并不支持使用super来找下一个类

    class A(object):
        def func(self):print('A')
    class B(A):
        def func(self):
            super().func()
            print('B')
    class C(A):
        def func(self):
            super().func()
            print('C')
    class D(B,C):
        def func(self):
            super().func()
            print('D')
    D().func()
    '''
    A
    C
    B
    D
    
    '''
  3. 其中 super().func() 也可以写成 super(D,self).func()

  4. 在单继承的程序中,super就是找父类:

    class User:
        def __init__(self,name):
            self.name = name
    class VIPUser(User):
        def __init__(self,name,level,strat_date,end_date):
            # User.__init__(self,name)
            # super().__init__(name)              # 推荐的
            # super(VIPUser,self).__init__(name)
            self.level = level
            self.strat_date = strat_date
            self.end_date = end_date
    
     '''
     解析:中间的# 的三种方式功能都是找到并执行父类中的__init__的方法,推荐使用
     super().__init__(name)。
     '''
        先找每个类的继承关系(注意因为加载顺序的缘故从,上至下开始寻找)
    
        A(0) = [AO]      # A 继承于object类 
    
        B(A) = [BAO]      # B继承于A类
    
        C(A) = [CAO]     # C继承于A类
    
        D(B) = [DBAO]       # D继承于B类
    
        E(C) = [ECAO]       # E继承于C类
    
        那么算法为:
    
        F(D,E) = merge(D(B)+E(C))
    
        ?    = [F] + [DBAO] + [ECAO]
    
        F = [DBAO] + [ECAO]
    
        FD = [BAO] + [ECAO]
    
        FDB = [AO] + [ECAO]
    
        FDBE = [AO] + [CAO]
    
        FDBEC = [AO] + [AO]  
    
        FDBECAO
    
      - **使用子类名.mro() 查看继承顺序**
    
        只有新式类中有,经典类没有的。
    1. 通过继承实现的类的开发规范
  5. 父类对子类的约束

    1. 普通类 如 class A B C等等

    2. 抽象类

      • 是一个开发的规范,约束他的所有子类必须实现一些和他同名的方法

      引入:例:不同付款方式付款

      class Wechat():
          def __init__(self,name):
              self.name = name
          def pay(self,money):
            print('%s通过微信支付%s钱成功'%(self.name,money))
      
      class Alipay():
          def __init__(self,name):
              self.name = name
          def pay(self,money):
              print('%s通过微信支付%s钱成功'%(self.name,money))
      
      class Apple(Payment):
          def __init__(self,name):
              self.name = name
          def pay(self,money):
              dic = 'name': self.name, 'number': money
              # 想办法调用苹果支付 url连接 把dic传过去
              print('%s通过苹果支付%s钱成功' % (self.name, money))
      
      aw = WeChat('alex')
      aw.pay(400)
      aa = Alipay('alex')
      aa.pay(400)
      '''
      这样我们通过创建了2个对象,然后调用类中的方法pay,把钱传入完成支付的功能
      '''

      当然我们有时需要归一化设计:

      def pay(name,price,kind):
          if kind == 'Wechat':
              obj = WeChat(name)
          elif kind == 'Alipay':
              obj = Alipay(name)
          elif kind == 'Apple':
              obj = Apple(name)
          obj.pay(price)
      pay('alex',400,'Wechat')
      pay('alex',400,'Alipay')
      pay('alex',400,'Apple')
      '''
      我们通过定义一个函数,通过判别传入的kind参数,选择创建实例化name对象,然后调用方法,完成付款。
      '''

      注意:完成上述需求需要三个类中的pay的方法名一样如果不一样就会报错。

      为解决上述问题引入了抽象类对子类进行相关的约束

      class Payment:
        def pay(self,money):
              raise NotImplementedError('请在子类中重写同名pay方法')
      class Alipay(Payment):pass
      class WeChat(Payment):pass    
      class Apple(Payment): pass
      def pay(name,price,kind):
          if kind == 'Wechat':
              obj = WeChat(name)
          elif kind == 'Alipay':
              obj = Alipay(name)
          elif kind == 'Apple':
              obj = Apple(name)
          obj.pay(price)
      '''
      解析:我们创建一个所有类的父类,定义一个pay的方法,如果执行,则抛出异常。
      当我们执行pay函数时,我们会调用方法 pay ,当对象中没有pay的方法时,就会去父类Payment中调用,所以抛出异常。所以加入抽象类约束我们子类中方法名称一致。
      
      '''
      
      
      # 另一种方式:依赖于abc 模块
      from abc import ABCMeta,abstractmethod
      class Payment(metaclass=ABCMeta):
          @abstractmethod
          def pay(self,money):
              '''只要你见到了项目中有这种类,你要知道你的子类中必须实现和pay同名的方法'''
              raise NotImplementedError('请在子类中重写同名pay方法')
      
      # 一旦子类中没有pay方法,实例化都无法完成。
      方式特点:约束力强。缺点:依赖于abc模块
      
  6. 多态

    1. python中处处是多态,一切皆对象

    2. 什么是多态,借助java讲解

      一个类型表现出来的多种状态

      在Java中:一个参数必须指定类型,所以如果想让俩个类型的对象都可以传,那么必须让这俩个类继承自一个父类,再指定类型的时候使用父类来指定

    3. 鸭子类型

      class list:
          def __len__(self):pass
      class dict:
          def __len__(self): pass
      class set:
          def __len__(self): pass
      class tuple:
          def __len__(self): pass
      class str:
          def __len__(self): pass
      def len(obj):
          return obj.__len__()
      # 如上述所有实现了__len__方法的类,在调用len函数的时候,obj都说是鸭子类型。
      # 再如:迭代器协议 __iter__ __next__ 是迭代器。即所有的迭代器都是鸭子类型

以上是关于面向对象02 继承的主要内容,如果未能解决你的问题,请参考以下文章

面向对象--02--封装继承多态

VSCode自定义代码片段——JS中的面向对象编程

VSCode自定义代码片段9——JS中的面向对象编程

day11-面向对象02

python基础之面向对象02

python基础之面向对象02