• 类的继承顺序-父类对子类的约束-多态-队列和栈


    一、类的继承顺序

    只要继承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):
            passclass 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)
    ​
    print('队列放进去的值:', q.ll)
    print('第一次取出:   ', q.get())
    print('第二次取出:   ', q.get())
    print('队列所剩值:   ', q.ll)
    print('------------------------------------')
    print('栈放进去的值: ', s.ll)
    print('第一次取出:   ', s.get())
    print('第二次取出:   ', s.get())
    print('栈所剩值:     ', s.ll)
    ​
    # 输出
    队列放进去的值: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    第一次取出:    0
    第二次取出:    1
    队列所剩值:    [2, 3, 4, 5, 6, 7, 8, 9]
    ------------------------------------
    栈放进去的值:  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    第一次取出:    9
    第二次取出:    8
    栈所剩值:     [0, 1, 2, 3, 4, 5, 6, 7]

    自定义Pickle,借助pickle模块来完成简化的dump和load

    • pickle dump

      • 打开文件

      • 把数据dump到文件里

    • pickle load

      • 打开文件

      • 读数据

    对象 = Mypickle('文件路径')

    对象.load() 能拿到这个文件中所有的对象

    对象.dump(要写入文件的对象)

    import pickle
    ​
    ​
    class Mypickle:
        def __init__(self, path):
            self.path_ = path
    ​
        def myload(self):
            with open(self.path_, mode='rb') as f1:
                while True:
                    try:
                        # 让读取到的数据变成迭代器
                        yield pickle.load(f1)
                    except EOFError:
                        breakdef mydump(self, count):
            with open(self.path_, mode='ab') as f2:
                pickle.dump(count, f2)
    ​
    # 需要放入文件的数据
    ll = [f'第{a}个' for a in range(3)]
    ​
    # 实例化一个对象
    obj = Mypickle(r'my_obj')
    obj.mydump(ll)      # 写入文件
    obj.myload()        # 读取文件的数据
    # 可以用__next__一条一条的读,也可以for循环读
    a = obj.myload().__next__()
    print(a)
    print('------------------------')
    # for循环读取迭代器内的数据
    for a in obj.myload():  
        print(a)
        
    # 输出
    ['第0个', '第1个', '第2个']
    ------------------------
    ['第0个', '第1个', '第2个']
    ['第0个', '第1个', '第2个']
    ['第0个', '第1个', '第2个']
    学习之旅
  • 相关阅读:
    [BZOJ1625][Usaco2007 Dec]宝石手镯
    [BZOJ1699][Usaco2007 Jan]Balanced Lineup排队
    [BZOJ1606][Usaco2008 Dec]Hay For Sale 购买干草
    [BZOJ1610][Usaco2008 Feb]Line连线游戏
    [BZOJ1609][Usaco2008 Feb]Eating Together麻烦的聚餐
    [BZOJ1602][Usaco2008 Oct]牧场行走
    [BZOJ1601][Usaco2008 Oct]灌水
    [BZOJ1607][Usaco2008 Dec]Patting Heads 轻拍牛头
    [BZOJ1579][Usaco2008 Mar]土地购买
    HDU 4248 A Famous Stone Collector 组合数学dp ****
  • 原文地址:https://www.cnblogs.com/XiaoYang-sir/p/14727845.html
Copyright © 2020-2023  润新知