当前位置 博文首页 > 小杨-先生:day-25-类的继承顺序-父类对子类的约束-多态-队列和

    小杨-先生:day-25-类的继承顺序-父类对子类的约束-多态-队列和

    作者:小杨-先生 时间:2021-05-03 18:14

    一、类的继承顺序

    只要继承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 ——> 新式类

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

    用的是深度优先方法

    寻找某一个方法的顺序是:D-->C-->B-->A

    越往父类走,是深度

    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()

     

    多继承方面

    • 广度优先——>在走到一个点,下一个点既可以从深度走,也可以从广度走的时候,总是先走广度,在走深度

    • 在经典类中,都是深度优先,总是在一条路走不通之后在换一条路,走过的点不会在走了

    • 在新式类中有 mro() ,可以查看寻找顺序

    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('D')
            
    d = D()
    d.func()
    print(D.mro())   # 只有在新式类中有,经典类没有
    # 输出
    D
    [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

     

    • C3算法:

    如果是单继承:那么总是按照从子类——>父类的顺序来计算查找顺序。

    如果是多继承:需要按照自己本类,父类1的继承顺序,父类2的继承顺序.......

    merge的规则(C3):

    1、如果一个类出现在从左侧到右所有顺序的最左侧,并且没有在其他位置出现,那么先提出来作为继承顺序的中的一个

    2、或 一个类出现在从左侧到右顺序的最左侧, 并没有在其他顺序中出现 ,那么先提出来作为继承顺序的中的一个

    3、如果从左到右第一个顺序中的第一个类出现在后面且不是第一个,那么不能提取,顺序向后继续找其他顺序中符合上述条件的类

    在多继承中:经典类——>是深度优先

    新式类——>是广度优先,遵循C3算法,可以用mro()查看顺序

    class A: pass
    class B(A): pass
    class C(A): pass
    class D(B): pass
    class E(C): pass
    class F(D, E): pass
    ?
    ?
    C3算法
    A(O) = [AO]     ——>A的继承关系 (O==>object)
    B(A) = [BAO]    ——>B的继承关系
    C(A) = [CAO]    ——>C的继承关系
    D(B) = [DBAO]   ——>D的继承关系
    E(C) = [ECAO]   ——>E的继承关系
    F(D,E)  = merge(D(B) + E(C))    ——>F的继承关系
    ?
    继承顺序  = [F] + [DBAO] + [ECAO]  ——>自己类加上两个父类的继承顺序
          F  = [DBAO] + [ECAO]      ——>取出左侧第一个F(条件右侧没有F)
         FD  = [BAO] + [ECAO]       ——>取出左侧第一个D(条件右侧没有D)
        FDB  = [AO] + [ECAO]        ——>左侧第一个A,右侧有A,跳过取右侧第一个E
       FDBE  = [AO] + [CAO]         ——>同上取右侧第一个C
      FDBEC  = [AO] + [AO]          ——>两边都是相同的取第一个A
     FDBECA  = [O] + [O]            ——>同上在取第一个O
    FDBECAO     ——>最终继承顺序

     

    二、父类对子类的约束

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

    列如:支付程序。

    • 微信支付 URL链接,告诉你参数什么格式

      • { ' username ' : ' 用户名 ' , ' money ' : 200 }

    • 支付宝支付 URL链接,告诉你参数什么格式

      • { ' uname ' : ' 用户名 ' , ' price' : 200 }

    方法一:

    class Payment:  # 这是个抽象类
        def pay(self, money):
            '''
            只要你见到了项目中有这种类,你要知道你的子类中必须实现和pay同名的方法
            '''
            raise NotImplementedError('请在类中重写重名pay类方法') # 主动抛异常
    ?
    class WeChat(Payment):
        def __init__(self, username):
            self.username = username
    ?
        def pay(self, money):  # pay方法名字不能改变
            dic = {'username': self.username, 'money': money}
            '''
            调用微信支付 url连接 把dic传过去
            '''
            print(f'{self.username}通过微信充值了{money}')
    ?
    class Alipay(Payment):
        def __init__(self, username):
            self.username = username
    ?
        def pay1(self, money):
            dic = {'uname': self.username, 'price': money}
            ''''
            调用支付宝支付 url连接 把dic传过去
            '''
            print(f'{self.username}通过支付宝充值了{money}')
    ?
    # 归一化设计:同事或用户使用此类时,直接调用pay函数传参,不用自己创建对象
    ?
    def pay(username, money, kind):
        if kind == 'WeChat':
            obj = WeChat(username)
        elif kind == 'Alipay':
            obj = Alipay(username)
        obj.pay(money)
    ?
    pay('小杨', 200, 'WeChat')
    ?
    # 当支付宝的pay方法名字发生改变时
    pay('小杨', 200, 'Alipay')
    ?
    # 输出
    小杨通过微信充值了200
    报错:NotImplementedError: 请在类中重写重名pay类方法

     

    方法二:实现抽象类的另一种方式,约束力强,依赖abc模块

    from abc import ABCMeta, abstractmethod
    ?
    class Payment(metaclass=ABCMeta):  # 这是个抽象类
        @abstractmethod
        def pay(self, money):
            pass
    ?
    class WeChat(Payment):
        def __init__(self, username):
            self.username = username
    ?
        def pay(self, money):  # pay方法名字不能改变
            dic = {'username': self.username, 'money': money}
            '''
            调用微信支付 url连接 把dic传过去
            '''
            print(f'{self.username}通过微信充值了{money}')
    ?
    class Alipay(Payment):
        def __init__(self, username):
            self.username = username
    ?
        def pay1(self, money):
            dic = {'uname': self.username, 'price': money}
            ''''
            调用支付宝支付 url连接 把dic传过去
            '''
            print(f'{self.username}通过支付宝充值了{money}')
    ?
    # 当支付宝的pay名字发生变化的时候
    Alipay('xiao')      # 这种方法在实例化对象的时候就会报错提示
    ?
    # 输出
    TypeError: Can't instantiate abstract class Alipay with abstract method pay

     

    三、多态

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

    • 同一个对象,多种形态。python默认支持多态

    def func(count):        # 这里的count可以是str、int、list、dict等等....count就是多态的
        print(count)
    ?
    ?
    func('abc')
    func(12345)
    func([1, 2, 3, 4])
    func({'a': 1, 'b': 2})
    # 输出
    abc
    12345
    [1, 2, 3, 4]
    {'a': 1, 'b': 2}

     

    而在Java的情况下:

    • 一个参数必须指定类型

    • 所以如果想两个类型的对象都可以传,那么必须让着两个继承自一个父类,在指定类型的时候使用父类来指定

    • 在java或者c#定义变量或者给函数传值必须定义数据类型,否则就报错。

    def func(int a):
        print('a必须是数学')

     

    • 而类似于python这种弱定义类语言,a可以是任意形态(str,int,object等等)。

    def func(a):
        print('a是什么都可以')

     

    python伪代码实现Java或C的多态

    class F1:
        pass
    ?
    ?
    class S1(F1):
        
        def show(self):
            print 'S1.show'
    ?
    ?
    class S2(F1):
        
        def show(self):
            print 'S2.show'
    ?
    ?
    # 由于在Java或C#中定义函数参数时,必须指定参数的类型
    # 为了让Func函数既可以执行S1对象的show方法,又可以执行S2对象的show方法,所以,定义了一个S1和S2类的父类
    # 而实际传入的参数是:S1对象和S2对象
    ?
    def Func(F1 obj):
    """Func函数需要接收一个F1类型或者F1子类的类型"""
    ?
        print obj.show()
        
    ?
    s1_obj = S1()
    Func(s1_obj)  # 在Func函数中传入S1类的对象 s1_obj,执行 S1 的show方法,结果:S1.show
    ?
    s2_obj = S2()
    Func(s2_obj)  # 在Func函数中传入Ss类的对象 ss_obj,执行 Ss 的show方法,结果:S2.show

     

    鸭子类型

    在python中,有一句谚语,你看起来像鸭子,那么你就是鸭子。

    对相同的功能设定了相同的名字,这样方便开发,这两个方法就可以互成为鸭子类型。

    比如:str、tuple、list 都有index方法,这就是互称为鸭子类型

    class A:
        def f1(self):
            print('in A f1')
        
        def f2(self):
            print('in A f2')
    ?
    ?
    class B:
        def f1(self):
            print('in A f1')
        
        def f2(self):
            print('in A f2')
            
    obj = A()
    obj.f1()
    obj.f2()
    ?
    obj2 = B()
    obj2.f1()
    obj2.f2()
    # A 和 B两个类完全没有耦合性,但是在某种意义上他们却统一了一个标准。
    # 输出
    in A f1
    in A f2
    in A f1
    in A f2

     

    四、队列和栈、自定义Pickle

    内置的数据结构:

    • {}:——key-value 通过key找v非常快

    • []:——序列 通过index取值非常快

    • ():——元组

    • {1,}:——集合

    • 'abc':——字符串

    不是python内置的:

    • Queue 队列:先进先出 FIFO (FIRST IN FIRST OUT)

      • put:进

      • get:出

    • Stack 栈:后进先出 LIFO (LAST IN FIRST OUT)

      • put:进

      • get:出

    class My_List:
        def __init__(self):
            self.ll = []
    ?
        def put(self, count):
            self.ll.append(count)
    ?
    ?
    class Stack(My_List):
        def get(self):
            return self.ll.pop()
    ?
    ?
    class Queue(My_List):
        def get(self):
            return self.ll.pop(0)
    ?
    ?
    q = Queue()
    ?
    s = Stack()
    ?
    for a in range(10):
        q.put(a)
        s.put(a)
    ?