ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

面向对象之继承

2020-08-08 21:33:19  阅读:189  来源: 互联网

标签:name 继承 self 面向对象 test print class def


class继承

一:继承介绍

什么是继承?

继承就是创建了一个新的类,在Python中,新创建的类可以继承一个或者多个父类(现实社会有点难)新建的类就是子类,围着派生类,父类称之为基类或者超类(累)

为什么要有继承?

继承可以减少代码冗余

如何使用继承?

class P1:
	pass

class P2:
    pass

class sub1(P1): # 继承了P1类
    pass

class sub2(P1,P2): # 继承了P1,P2类
    pass

通过类内的__bases__可以查看类继承的所有的父类

print(sub1.__bases__) # (<class '__main__.P1'>,)
print(sub2.__bases__) # (<class '__main__.P1'>, <class '__main__.P2'>)

示列一(继承)

class School:
    scholl = '东京校区'
    
class Student(School):
    
    def __init__(self,name,age,gender,suid,course):
        self.name = name
        self.age = age
        self.gender = gender
        self.suid = suid
        self.course = course
    
    def choose(self):
        print(f"{self.name}正在选课")
    
     
class Teacher(School):
    
    def __init__(self,name,age,gender,salary,level):
        self.name = name
        self.age = age
        self.gender = gender
        self.salary = salary
        self.level = level
        
    def score(self,student,num):
        student.num = num
        
s1 = Student('alen',66,'male',10001,'python开发')
t1 = Teacher('eg',18,'male',2000,10)

print(t1.scholl)    # 东京校区
print(s1.__dict__)  # {'name': 'alen', 'age': 66, 'gender': 'male', 'suid': 10001, 'course': 'python开发'}
print(t1.__dict__)  # {'name': 'eg', 'age': 18, 'gender': 'male', 'salary': 2000, 'level': 10}

二:如何在子类中派生新方法重用父类的功能

方式一:指名道姓地调用某一个类的函数

特点:可以不以依赖继承关系

示列

class School:
    scholl = '东京校区'
    
    def __init__(self,name,age,gender):
        self.name = name
        self.age = age
        self.gender = gender
    
    
    def t1(self):
        print("这是一个测试继承的函数")
    
class Student(School):
    
    def __init__(self,name,age,gender,suid,course):
        School.__init__(self,name,age,gender)
        self.suid = suid
        self.course = course
    
    def choose(self):
        print(f"{self.name}正在选课")
    
     
class Teacher(School):
    
    def __init__(self,name,age,gender,salary,level):
     	School.__init__(self,name,age,gender)
        self.salary = salary
        self.level = level
        
    def score(self,student,num):
        School.t1(self)
        student.num = num
        
s1 = Student('alen',66,'male',10001,'python开发')
t1 = Teacher('eg',18,'male',2000,10)
print(s1.__dict__)   # {'name': 'alen', 'age': 66, 'gender': 'male', 'suid': 10001, 'course': 'python开发'}
print(t1.__dict__)  # {'name': 'eg', 'age': 18, 'gender': 'male', 'salary': 2000, 'level': 10}

三:属性查找

在python中如果对象内部没有属性,就去类里去找,如果类里没有就去父类找(如果没有有继承,就找不到报错)

在python3中会去object里去找,(如果没有默认继承,默认就是继承object类),在python2中,存在两种类(新式类和经典类),两种类的查找循序有点区别

示列

class  Foo:
    
    def f2(self):
        print("from foo.f2")
    
    def f1(self):
        print("from foo.f1")
        self.f2()
        
class Bar(Foo):
    def f2(self):
        print("from Bar.f2")

t = Bar()
t.f1() 
"""
from foo.f1
from Bar.f2
"""

父类如果不想让子类覆盖自己的方法,可以在方法名前加__,让其成为私有属性

class  Foo:
    
    def __f2(self):
        print("from foo.f2")
    
    def f1(self):
        print("from foo.f1")
        self.__f2()
        
class Bar(Foo):
    def __f2(self):
        print("from Bar.f2")

obj = Bar()
obj.f1()
​```
from foo.f1
from foo.f2
​```

四:继承的原理

基础知识

新式类:凡是继承了object的子类,以该子类子子孙孙类都称之为新式类

经典类:没有继承object的子类,以该子类子子孙孙类都称之为经典类

在python3中全都是新式类,python2中有经典类

在python3中没有继承任何类,默认基础obj类

在py3中
class Foo:
	pass

print(Foo.__bases__) # (<class 'object'>,)
在py2中
class Foo:
	pass
print(Foo.__bases__) # 为空

继承的实现原理

1:菱形问题

大多数面向对象语言都不支持多继承,在python中,一个子类是可以同时继承多个父类的,这个可以让一个子类,可以对多个不同父类加以重用的好处,但是也有可能引发著名的菱形问题(或称死亡钻石问题)

如图

![菱形继承](D:\Program Files (x86)\markdown\python3.8\菱形继承.png)

A类在顶部,B类和C类分别位于其下方,D类在底部将两者连接在一起形成菱形。

这个图展示的就是菱形继承

如果A中有一个方法,B或者C都重写了该方法,而D没有重写,那么D继承的是哪个版本的方法:是B或者是C?看下面

在py3中
class A:
    def test(self):
        print('from A')
    
class B(A):
    def test(self):
	print('from B')
    
class C(A):
    def test(self):
	print('from C')

class D(B,C):
    pass
                                    
obj = D
obj.test()  # 结果为from B

要想知道obj.test()是如何找到方法test的,需要了解Python的继承实现原理--

2继承原理

MRO

Python是如何实现继承的呢?

对于你定义的每一个类,python都会计算出方法解析顺(mro)列表,该mro列表就是一个简单的所有基类的线性顺序列表,如下

print(D.mro())
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

python会在mro列表上从左到右开始查找基类,直到找到一个第一匹配这个属性的类为止

这个mro列表的构造是通过一个C3线性算法来实现的。

这个算法实际上就是合并所有父类的mro列表并遵循如下原则

1:子类会先于父类查找
2:多个父类会根据它们所在列表中的顺序被检查
3:如果对下一个类存在两个合法的选择,选择第一个父类

属性查找

1:由对象发起的属性查找,会从对象自身的属性里检索,没有则会按照对象的类的mro列表规定的顺序依次找下去
2:由类发起的属性查找,会按照当前类.mro规定的顺序依次找下去

示列

#【test1】

class A(object):
    def test(self):
        print('from A')

class B(A):
    def test(self):
        print('from B')


class C(A):
    def test(self):
        print('from C')

class D(C,B):
    def test(self):
        print('from D')

print(D.mro())  # 类D以及类D的对象访问属性都是参照该类的mro列表
# 输出结果:
# [<class '__main__.D'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
obj1 = D()
obj1.test()   # 输出结果:from D
print(D.test)  # 输出结果:<function D.test at 0x0000012EA27FA4C0>


print(C.mro()) # 类C以及类C的对象访问属性都是参照该类的mro列表
# 输出结果:[<class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
obj2 = C()
obj2.test()  # 输出结果:from C
print(C.test) # 输出结果: <function C.test at 0x0000022FA7D0A3A0>

# 【test2】
class A(object):
    # def test(self):
    #     print('from A')
    pass

class B(A):
    def test(self):
        print('from B')

class C(A):
    # def test(self):
    #     print('from C')
    pass
class D(C,B):
    # def test(self):
    #     print('from D')
    pass

print(D.mro())  # 类D以及类D的对象访问属性都是参照该类的mro列表
# 输出结果:
# [<class '__main__.D'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
obj1 = D()
obj1.test()   # 输出结果:from B
print(D.test)  # 输出结果:<function B.test at 0x00000246D6F3A310>

# 总结:类相关的属性查找(类名.属性,该类的对象.属性),都是参照该类的mro

3:深度优先和广度优先

1:非菱形继承

如果多继承是非菱形继承,经典类与新式类的属性查找顺序一样:都是一个分支一个分支地找下去,然后找object类

示列如下

![image-20200807201837419](D:\Program Files (x86)\markdown\python3.8\image-20200807201837419.png)

代码演示

class E:
    # def test(self):
    #     print('from E')
    pass

class F:
    def test(self):
        print('from F')


class B(E):
    # def test(self):
    #     print('from B')
    pass

class C(F):
    # def test(self):
    #     print('from C')
    pass

class D:
    def test(self):
        print('from D')


class A(B, C, D):
    # def test(self):
    #     print('from A')
    pass

# 新式类
print(A.mro())  
# A->B->E->C->F->D->object 
# [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, 
# <class '__main__.C'>, <class '__main__.F'>, <class '__main__.D'>, <class 'object'>]

obj = A()
obj.test()  # 结果为:from F

2:菱形继承

如果多继承是菱形继承,经典类与新式类的属性查找顺序不一样

经典类:深度优先,会在在检索第一个分支的时候就直接一天道走到黑,即会检索(共同的父类)

新式类:广度优先,会在检索最后一条分支的时候检索共同的父类

![image-20200807202406380](D:\Program Files (x86)\markdown\python3.8\image-20200807202406380.png)

class A(object):
    def test(self):
        print('from A')


class B(A):
    def test(self):
        print('from B')


class C(A):
    def test(self):
        print('from C')


class D(B,C):
    pass


obj = D()
obj.test() # 结果为:from B
# 如果B没有去找C,c没有然后找A
python到底是如何实现继承的呢? 对于你定义的每一个类,Python都会计算出一个方法解析顺序(MRO)列表,该MRO列表就是一个简单的所有基类的线性顺序列表,如下
print(d.mro) # [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
class G:   # 在python2中,未继承object的类及其子类,都是经典类
    # def test(self):
    #     print('from G')
    pass

class E(G):
    # def test(self):
    #     print('from E')
    pass

class F(G):
    def test(self):
        print('from F')

class B(E):
    # def test(self):
    #     print('from B')
    pass

class C(F):
    def test(self):
        print('from C')

class D(G):
    def test(self):
        print('from D')

class A(B,C,D):
    # def test(self):
    #     print('from A')
    pass

# # 新式类
print(A.mro())
# # A->B->E->C->F->D->G->object
# # [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.F'>,
# # <class '__main__.D'>, <class '__main__.G'>, <class 'object'>]
obj = A()
obj.test()  # 输出结果:from C


# 经典类:A->B->E->G->C->F->D
print(A.mro())
obj = A()
obj.test()  # 输出结果:from C

3:总结

多继承到底要不要用?
python既然提供了,那么就要用,但是要规避问题
1:继承结构尽量不要过于复杂
2:推荐使用mixins机制:在多继承的背景下满足继承是什么关系

四:python Mixins机制

用来解决多继承所带来的问题的,将原本的混乱的继承清晰的变成’is-a‘的关系

# 民航飞机、直升飞机、汽车都是交通工具,但是飞机会飞,汽车不会,
# 这个时候我们将飞这个功能放到交通工具中就显得不合适
class Vehicle:  # 交通工具
    def fly(self):
        '''
        飞行功能相应的代码        
        '''
        print("I am flying")


class CivilAircraft(Vehicle):  # 民航飞机
    pass


class Helicopter(Vehicle):  # 直升飞机
    pass


class Car(Vehicle):  # 汽车并不会飞,但按照上述继承关系,汽车也能飞了
    pass

# 针对上面的问题,就引入Mixin机制
# Mixin机制指的就是子类混合不同类的功能,然后将其用统一的命名规范来标识该类只是用来混合功能的
# 从而来遵守多继承下的“is-a”关系,Mixin机制本质仍是多继承
class Vehicle:  # 交通工具
	pass

class FlyMixin:
    def fly(self):
        '''
        飞行功能相应的代码        
        '''
        print("I am flying")


class CivilAircraft(FlyMixin,Vehicle):  # 民航飞机
    pass


class Helicopter(FlyMixin,Vehicle):  # 直升飞机
    pass


class Car(Vehicle):  # 汽车并不会飞,但按照上述继承关系,汽车也能飞了
    pass

'''
使用Mixin类实现多重继承要非常小心
1、它必须表示某一种功能,而非物品,python 对于mixin类的命名方式一般以 Mixin, able, ible 为后缀
2、它必须责任单一,如果有多个功能,那就写多个Mixin类,一个类可以继承多个Mixin,为了保证遵循继承
   的“is-a”原则,只能继承一个标识其归属含义的父类
3、然后,它不依赖于子类的实现
4、最后,子类即便没有继承这个Mixin类,也照样可以工作,就是缺少了某个功能。
  (比如飞机照样可以载客,就是不能飞了)
'''

五:派生与方法重用

  • 子类中衍生出的新东西

    1. 子类独有,父类没有
    2. 子类有,父类也有,子类是完全覆盖父类
    3. 子类有,父类也有,子类在父类的基础上进行扩展
    
    

class People:
... school='清华大学'
...
... def init(self,name,sex,age):
... self.name=name
... self.sex=sex
... self.age=age
...

class Teacher(People):
... def init(self,name,sex,age,title): # 派生
... self.name=name
... self.sex=sex
... self.age=age
... self.title=title
... def teach(self):
... print('%s is teaching' %self.name)

```
  • 想在子类派生出的方法内重用父类的功能,有两种实现方式

    # 方法一:“指名道姓”地调用某一个类的函数=》不依赖于继承
    >>> class Teacher(People):
    ...     def __init__(self,name,sex,age,title):
    ...         People.__init__(self,name,age,sex) #调用的是函数,因而需要传入self
    ...         self.title=title
    ...     def teach(self):
    ...         print('%s is teaching' %self.name)
    
    # 方法二:super()调用父类提供给自己的方法=》严格依赖继承关系
    # 调用super()会得到一个特殊的对象,该对象会按照发起属性查找的类的mro去当前类的父类中找属性
    >>> class Teacher(People):
    ...     def __init__(self,name,sex,age,title):
    ...         super().__init__(name,age,sex) #调用的是绑定方法,自动传入self
    ...         self.title=title
    ...     def teach(self):
    ...         print('%s is teaching' %self.name)
    
    
    >>> #A没有继承B
    ... class A:
    ...     def test(self):
    ...         super().test()
    ... 
    >>> class B:
    ...     def test(self):
    ...         print('from B')
    ... 
    >>> class C(A,B):
    ...     pass
    ... 
    >>> C.mro() # 在代码层面A并不是B的子类,但从MRO列表来看,属性查找时,就是按照顺序C->A->B->object,B就相当于A的“父类”
    [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>,<class ‘object'>]
    >>> obj=C()
    >>> obj.test() # 属性查找的发起者是类C的对象obj,所以中途发生的属性查找都是参照C.mro()
    from B
    

    六:组合

    什么是组合?

    • 一个类中以另外一个类的对象作为数据属性,作为类的组合
    # 组合与继承都是用来解决代码的重用性问题。
    # 二者的区别在于
    '''
    继承:强调‘是’的关系,例如老师是人、学生是人,当类之间有很多相同的之处,应该使用继承
    组合:强调‘有’的关系,例如老师有多门课程,老师有给学生打分,当类之间有显著不同,并且较小的类是较大的类所需
    要的组件时,应该使用组合
    '''
    class Course:
        def __init__(self,name,period,price):
            self.name=name
            self.period=period
            self.price=price
        def tell_info(self):
            print('<%s %s %s>' %(self.name,self.period,self.price))
    
    class Date:
        def __init__(self,year,mon,day):
            self.year=year
            self.mon=mon
            self.day=day
        def tell_birth(self):
           print('<%s-%s-%s>' %(self.year,self.mon,self.day))
    
    class People:
        school='清华大学'
        def __init__(self,name,sex,age):
            self.name=name
            self.sex=sex
            self.age=age
    
    #Teacher类基于继承来重用People的代码,基于组合来重用Date类和Course类的代码
    class Teacher(People): #老师是人
        def __init__(self,name,sex,age,title,year,mon,day):
            super().__init__(name,age,sex)
            self.birth=Date(year,mon,day) #老师有生日
            self.courses=[] #老师有课程,可以在实例化后,往该列表中添加Course类的对象
        def teach(self):
            print('%s is teaching' %self.name)
    
    
    python=Course('python','3mons',3000.0)
    linux=Course('linux','5mons',5000.0)
    teacher1=Teacher('lili','female',28,'博士生导师',1990,3,23)
    
    # teacher1有两门课程
    teacher1.courses.append(python)
    teacher1.courses.append(linux)
    
    # 重用Date类的功能
    teacher1.birth.tell_birth()
    
    # 重用Course类的功能
    for obj in teacher1.courses: 
        obj.tell_info()
    

参考

https://zhuanlan.zhihu.com/p/109331525

https://www.cnblogs.com/linhaifeng/articles/7340153.html

标签:name,继承,self,面向对象,test,print,class,def
来源: https://www.cnblogs.com/wait59/p/13460396.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有