zoukankan      html  css  js  c++  java
  • PYTHON3中 类的继承(转帖)

    原帖地址:https://www.cnblogs.com/lx3822/p/8808845.html

     

    继承

    1:什么是继承

    继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,也就是说在python中支持一个儿子继承多个爹。

    新建的类成为子类或者派生类。

    父类又可以成为基类或者超类。

    子类会遗传父类的属性。

    2:为什么要用继承

    减少代码冗余(也就是重复写代码)。

    3:怎么用继承:

    复制代码
    我们定义两个类;
    class parenclass1:
            pass
    
    class parenclass2:
            pass
    
    
    在定义两个类:
    
    class subclass1:
    
        pass
    
    class subclass2:
        pass
    
    我想让 :
    class parenclass1:      作为    class  subclass1:     的父类。   
        pass                            pass
    

    应该这样用: class subclass1( parenclass1): 这就表示subclass1是子类,parenclass 是subclass1 的父类 pass
    两个父类的话怎么表达呢?如下: class subclass2(parenclass1,parenclass2): pass 这就表示subclass2的父类是parenclass1,parenclass2 这两个
    复制代码

     

    想要查看子类的父类 应该怎样查看呢: 用__bases__  如下:

    复制代码
    class ParentClass1:
        pass
    
    class ParentClass2:
        pass
    
    class Subclass1(ParentClass1):
        pass
    
    class Subclass2(ParentClass1,ParentClass2):
        pass
    
    
    print(Subclass1.__bases__) 
    #打印结果:(<class '__main__.ParentClass1'>,)
    
    print(Subclass2.__bases__)
    #打印结果:
    (<class '__main__.ParentClass1'>, 
     <class '__main__.ParentClass2'>)
    复制代码

    经典类与新式类

    复制代码
    1、只有在python2中才分新式类和经典类,python3中统一都是新式类
    2、在python2中 没有显示继承的object类的类,以及该类的子类都是经典类
    3、在python2中,显示的声明继承object的类,以及该类的子类都是新式类
    4、在python3中,无论是否继承object,都默认 继承object,即python3中所有类均为新式类
    
    至于经典类 与新式类的区别,后面会有讨论。
    
    提示:如果没有指定基类, python的类会默认继承object类, object是所有python类的基类。
    复制代码

    二、继承与抽象

    继承描述的是子类与父类之间的关系,是一种什么的关系呢? 要找出这种关系, 必须先抽象在继承。

    抽象即抽取类似或者说比较像的部分。

    抽象分成两个层次:

    1.将奥巴马和梅西这俩对象比较像的部分抽取成类; 

    2.将人,猪,狗这三个类比较像的部分抽取成父类。

    抽象最主要的作用是划分类别(可以隔离关注点,降低复杂度)

    继承:是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象合格过程, 才能通过继承的方式去表达抽象的结构。

    抽象只是分析和设计的过程,一个动作或者说一种技巧,通过抽象可以得到类

     例如:我们写一个老男孩的老师与学生的类,若是不涉及到继承的话  我们正常是这样写

    复制代码
    class OldboyTeacher:
        school = 'oldboy'
    
        def __init__(self,name,age,sex):
            self.name=name
            self.age=age
            self.sex=sex
    
        def change_score(self):
            print('teacher %s is changing score ' %self.name)
    
    class Oldboystudent:
        school = 'oldboy’
        def __init__(self,name,age,sex):
            self.name=name
            self.age=age
            self.sex=sex
    
        def choose(self):
            print('student %s choose course' %self.name)
    
    tea1 = OldboyTeacher('egon', 18, 'male') #OldboyTeacher.__init__(...)
    stu1=Oldboystudent('alex',73,'female')
    
    print(tea1.name,tea1.age,tea1.sex) # egon 18 male

    print(stu1.name) #alex
    复制代码

    但是我们经过分析 发现里面里面有许多重复代码, 这时我们可以用到类的继承来写了。如下:

    复制代码
    class OldboyPeople:
        school ='oldboy'
    
        def __init__(self,name,age,sex):
            self.name=name
            self.age=age
            self.sex=sex
    
    class Oldboyteacher(OldboyPeople):
        def change_score(self):
            print('teacher %s is changing score ' %self.name)
    class Oldboystudent(OldboyPeople): def choose(self): print('student %s choose course'%self.name) tea1 = Oldboyteacher('egon', 18, 'male') stu1=Oldboystudent('alex',73,'female')
    print(tea1.name,tea1.age,tea1.sex)#egon 18 male print(stu1.name) #alex
    复制代码

     三、基于继承在看属性查找

    我们先看一个列子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class Foo:
        def f1(self):
            print('Foo.f1')
     
        def f2(self): #self=obj
            print('Foo.f2')    #在父类中找到发 f2属性,第3步打印这一行
            self.f1() #obj.f1()  第4步再去掉用self的f1属性
     
    class Bar(Foo):
        def f1(self):#第五步, 在回到object自身的名称空间找f1属性,找到后调用
            print('Bar.f1') #第6步 执行
     
    obj=Bar() #第一步 :类的实例化, 先得到一个空对象,
     
    obj.f2()  #第2步:空对象调用f2属性 在自身寻找f2属性, 没有找到就去父类中寻找<br><br>#结果

    Foo.f2
    Bar.f1

    注意子类的属性查找,一定是优先查找子类自己本身的属性与特征, 在本身没有的情况下 在去父类中查找。

    这个还是非常有意思的,当执行obj.f2时,自己没有该属性,寻找父类属性,当找到f2属性时,执行self.f1,又优先取寻找自己的self.f1属性了,确实够贱。

    self很重要,哪个self进去,属性都是先找self身上自己的。

    四、派生

    派生:子类定义自己新的属性,如果与父类同名,以子类自己的为准。

    复制代码
    class OldboyPeople:
        school = 'oldboy'
    
        def __init__(self, name, age, sex):
            self.name = name
            self.age = age
            self.sex = sex
    
        def f1(self):
            print('爹的f1')
    class OldboyTeacher(OldboyPeople):
        def change_score(self):
            print('teacher %s is changing score' %self.name)
    
        def f1(self):
            print('儿子的f1')
    
    tea1 = OldboyTeacher('egon', 18, 'male')
    tea1.f1()

    #调用显示:儿子的f1
    # 父类和子类中都有f1, 优先调用自己的属性,所以结果调用的是儿子的f1
    复制代码

    五、在子类中派生出的新方法重用父类的功能

    拿上一案例来举例 在oldboyteacher 这个类中要添加薪水与级别。 然后调用。 有两种方式。

    方式一:指名道姓的调用(与继承没有什么关系)

    复制代码
    class OldboyPeople:
        school ='oldboy'
    
        def __init__(self,name,age,sex):
            self.name=name
            self.age=age
            self.sex=sex
    
        def tell_info(self):
            print(
                '''
                ====个人信息====
               姓名:%s
               年龄:%s
               性别:%s
                '''%(self.name,self.age,self.sex))
    
    class OldboyTeacher(OldboyPeople):
    
        def __init__(self,name,age,sex,level,salary):
            OldboyPeople.__init__(self,name,age,sex)  #在这里指明道姓来调用这一个函数里的属性
    
            self.level =level
            self.salary=salary
    
        def tell_info(self):
            OldboyPeople.tell_info(self)   #指名道姓的来调用这个函数里的属性
            print("""
            等级:%s
            薪资:%s
            """ %(self.level,self.salary))
    
    tea1 = OldboyTeacher('egon', 18, 'male', 9, 3.1)
    print(tea1.name, tea1.age, tea1.sex, tea1.level, tea1.salary)
    
    
    tea1.tell_info()
    
    #打印结果:
    egon 18 male 9 3.1
    
                ====个人信息====
               姓名:egon
               年龄:18
               性别:male
                
    
            等级:9
            薪资:3.1
    复制代码

    方法二、

    用super()调用(严格依赖于继承)

    super() 的返回值是一个特殊的对象,该对象专门用来调用父类中的属性, 一般在python2中,需要super(自己的类名,self),  而python3中,括号里面一般不填类名

    复制代码
    class OldboyPeople:
        school = 'oldboy'
    
        def __init__(self, name, age, sex):
            self.name = name
            self.age = age
            self.sex = sex
    
        def tell_info(self):
            print("""
            ===========个人信息==========
            姓名:%s
            年龄:%s
            性别:%s
            """ %(self.name,self.age,self.sex))
    
    
    class OldboyTeacher(OldboyPeople):
    
        def __init__(self, name, age, sex, level, salary):
            super().__init__(name,age,sex)
    
            self.level = level
            self.salary = salary
    
        def tell_info(self):
            super().tell_info()
            print("""
            等级:%s
            薪资:%s
            """ %(self.level,self.salary))
    
    tea1 = OldboyTeacher('egon', 18, 'male', 9, 3.1)
    print(tea1.name, tea1.age, tea1.sex, tea1.level, tea1.salary)
    tea1.tell_info()
    
    #调用结果:
    egon 18 male 9 3.1
    
            ===========个人信息==========
            姓名:egon
            年龄:18
            性别:male
            
    
            等级:9
            薪资:3.1
            
    复制代码

    六:经典类 与新式类

    1新式类:

    继承object的类,以及该类的子类,都是新式类

    在python3中,如果一个类没有指定继承的父类,默认就继承object

    所以说在python3中所有的类都是新式类

     

    2经典类:(只有在python2才区分经典类和新式类):

    没有继承object的类,以及该类的子类 都是经典类

    1 经典类:深度优先

    2 新式类:广度优先

    如果继承关系为非菱形结果吗则会按照先找B 这一条分支,然后在找c这一条分支,最后找D这一条分支的顺序,直到找到我们想要的属性

    当继承关系为菱形结构时

    经典类查找顺序:

    若是在A 类里自己没找到, 则会先去B类里去找, B类里没找到,就会在E类里找, 然后在G类里找,

    G类里没找到 会去C 类里找, 然后去F 类里找,最后去D 类里找。

    新式类查找顺序: 

    按照图中1 ,2 , 3, 4,5,6的顺序查找, 这个为广度优先的查找方式

    这个图 蛮好的,缺少了两个线,应该E(G)与F(G)也指向G(object),其实对于新式类的继承,基于广度与深度,我自己根本无法定论。

    C3算法我没去仔细研究,但根据我自己的经验来看,会根据继承的类的顺序与深度相关。

    下面上我自己写的测试代码:

    class A(object):
        def ping(self):
            print('ping', self)
    
    
    class B(A):
        def pong(self):
            print('pong', self)
    
    
    class E(B):
        ...
    
    
    
    
    class F(object):
        ...
    
    class H(F):
        ...
    
    class C(H):
        def pong(self):
            print('PONG', self)
    
    class D(C, E):
    
        def ping(self):
            super(D, self).ping()
            print('post-ping', self)
    
    
    
    if __name__ == '__main__':
        print(D.__mro__)
    
    /usr/local/bin/python3.7 /Users/shijianzhong/study/Fluent_Python/第十二章/diamond.py
    (<class '__main__.D'>, <class '__main__.C'>, <class '__main__.H'>, <class '__main__.F'>, <class '__main__.E'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
    
    Process finished with exit code 0
    

     从运行结果来看,C线继承到object之前,一致沿着C走,后面再沿着E走。

    object是两个父类的相交的基类结合点。

    class A(object):
        def ping(self):
            print('ping', self)
    
    
    class B(A):
        def pong(self):
            print('pong', self)
    
    
    class E(B):
        ...
    
    
    
    
    class F(object):
        ...
    
    class H(A):
        ...
    
    class C(H):
        def pong(self):
            print('PONG', self)
    
    class D(C, E):
    
        def ping(self):
            super(D, self).ping()
            print('post-ping', self)
    
    
    
    if __name__ == '__main__':
        print(D.__mro__)
    
    /usr/local/bin/python3.7 /Users/shijianzhong/study/Fluent_Python/第十二章/diamond.py
    (<class '__main__.D'>, <class '__main__.C'>, <class '__main__.H'>, <class '__main__.E'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
    
    Process finished with exit code 0
    

     我把上面代码的H继承到了A,这样D的两个父类的基类相交点到A了,从继承来看,先寻找C线到A之前的类,然后再用E寻找到最后的object。

    七:  super()依赖继承

    super()会严格按照mro列表从当前查找到的位置继续往后查找

    复制代码
    class A:
        def test(self):
            print('A.test') #2 执行这一步 打印
            super().f1 #3 然后在调用父类里的f1,   根据C.mro里的查找顺序执行到A 往后继续执行到B里去查找
    
    class B:
        def f1(self): #4找到f1, 执行
            print('from B')  #5打印
    
    class C(A,B):
        pass
    
    c=C()
    print(C.mro())  #调用属性的顺序  [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
    
    c.test() #1:C里没有 ,去A里调用

    #打印结果

    A.test

    from B

    复制代码
  • 相关阅读:
    丑数(摘)
    queue 之团队队列(摘)
    stack 集合栈计算机 (摘)
    反片语(map)
    stl的集合set——安迪的第一个字典(摘)
    stringstream函数(i o)
    T
    S
    R
    java面试题
  • 原文地址:https://www.cnblogs.com/sidianok/p/12132713.html
Copyright © 2011-2022 走看看