前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >python面向对象之继承与派生

python面向对象之继承与派生

作者头像
菲宇
发布2019-06-13 15:29:40
4710
发布2019-06-13 15:29:40
举报
文章被收录于专栏:菲宇菲宇

一、 继承顺序

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32

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): def test(self): print('from D') class E(C): def test(self): print('from E') class F(D,E): # def test(self): # print('from F') pass f1=F() f1.test() print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性 #新式类继承顺序:F->D->B->E->C->A #经典类继承顺序:F->D->B->A->E->C #python3中统一都是新式类 #pyhon2中才分新式类与经典类

二、 继承原理(python如何实现的继承)

python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表,例如

1 2

>>> F.mro() #等同于F.__mro__ [<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>,<br> <class 'object'>]

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

合并所有父类的MRO列表并遵循如下三条准则: 1.子类会先于父类被检查 2.多个父类会根据它们在列表中的顺序被检查 3.如果对下一个类存在两个合法的选择,选择第一个父类

三、子类中调用父类方法(super()方法)

子类继承了父类的方法,然后想进行修改,注意了是基于原有的基础上修改,那么就需要在子类中调用父类的方法

方法一 父类名.父类方法()

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

class People: def __init__(self,name,sex,age): self.name=name self.sex=sex self.age=age def walk(self): print('%s is walking'%self.name) class Chinese(People): country='China' def __init__(self,name,sex,age,language='Chinses'): People.__init__(self,name,sex,age) self.language=language def walk(self,x): pass c=Chinese('xiaojing','male',20) print(c.name,c.sex,c.age,c.language)

方法二 super()方法

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

class People: def __init__(self,name,sex,age): self.name=name self.age=age self.sex=sex def walk(self): print('%s is walking'%self.name) class Chinese(People): country='China' def __init__(self,name,sex,age,language='Chinese'): super().__init__(name,sex,age) #super() 绑定 调用父类方法 self.language=language def walk(self,x): super().walk() print("---->子类的x",x) c=Chinese('EGG','male','20') c.walk(123)

不用super引发的惨案

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

#每个类中都继承了且重写了父类的方法 class A: def __init__(self): print('A的构造方法') class B(A): def __init__(self): print('B的构造方法') A.__init__(self) class C(A): def __init__(self): print('C的构造方法') A.__init__(self) class D(B,C): def __init__(self): print('D的构造方法') B.__init__(self) C.__init__(self) pass f1=D() print(D.__mro__) #python2中没有这个属性

当你使用super()函数时,Python会在MRO列表上继续搜索下一个类。只要每个重定义的方法统一使用super()并只调用它一次,那么控制流最终会遍历完整个MRO列表,每个方法也只会被调用一次(注意注意注意:使用super调用的所有属性,都是从MRO列表当前的位置往后找,千万不要通过看代码去找继承关系,一定要看MRO列表

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

#每个类中都继承了且重写了父类的方法 class A: def __init__(self): print('A的构造方法') class B(A): def __init__(self): print('B的构造方法') super(B,self).__init__() class C(A): def __init__(self): print('C的构造方法') super(C,self).__init__() class D(B,C): def __init__(self): print('D的构造方法') super(D,self).__init__() f1=D() print(D.__mro__) #python2中没有这个属性

一、什么是继承:

继承是一种创建新的类的方式,在python中,新建的类可以继承自一个或者多个父类,原始类成为基类或超累,新建的类成为派生类或子类

1.1 继承分为:单继承和多继承

1 2 3 4 5 6 7 8 9 10 11

class ParentClass1:#定义父类 pass class ParentClass2:#定义父类 pass class SubClass1(ParentClass1):#单继承,基类是parentClass1,派生类是SubClass pass class SubClass2(ParentClass1,ParentClass2):#python支持多继承,用逗号分割开多个继承的类 pass

1.2 查看继承 

代码语言:javascript
复制
>>> SubClass1.__bases__
(<class '__main__.ParentClass1'>,)
>>> SubClass2.__bases__
(<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)

提示:如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如__str__)的实现。

1 2 3 4

>>> ParentClass1.__bases__ (<class 'object'>,) >>> ParentClass2.__bases__ (<class 'object'>,)

1 为什么要封装

封装数据的主要原因是:保护隐私

封装方法的主要原因是:隔离复杂度

2 封装分为两个层面

第一个层面的封装(什么都不用做):创建类和对象会分别创建二者的名称空间,我们只能用类名.或者obj.的方式去访问里面的名字,这本身就是一种封装

1 2 3 4

>>> r1.nickname '草丛伦' >>> Riven.camp 'Noxus'

注意:对于这一层面的封装(隐藏),类名.和实例名.就是访问隐藏属性的接口

第二个层面的封装:类中把某些属性和方法隐藏起来(或者说定义成私有的),只在类的内部使用、外部无法访问,或者留下少量接口(函数)供外部访问。

在python中用双下划线的方式实现隐藏属性(设置成私有的)

类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式:

1 2 3 4 5 6 7 8

class A: __N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N def __init__(self): self.__X=10 #变形为self._A__X def __foo(self): #变形为_A__foo print('from A') def bar(self): self.__foo() #只有在类内部才可以通过__foo的形式访问到.

这种自动变形的特点:

1.类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果。

2.这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的。

2.在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。

注意:对于这一层面的封装(隐藏),我们需要在类中定义一个函数(接口函数)在它内部访问被隐藏的属性,然后外部就可以使用了

这种变形需要注意的问题是:

1.这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N

1 2 3 4 5 6 7

>>> a=A() >>> a._A__N 0 >>> a._A__X 10 >>> A._A__N 0

2.变形的过程只在类的定义是发生一次,在定义后的赋值操作,不会变形 

3.在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的

1 2 3 4 5 6 7 8 9 10 11 12 13 14

#正常情况 >>> class A: ... def fa(self): ... print('from A') ... def test(self): ... self.fa() ... >>> class B(A): ... def fa(self): ... print('from B') ... >>> b=B() >>> b.test() from B

1 2 3 4 5 6 7 8 9 10 11 12 13 14

#把fa定义成私有的,即__fa >>> class A: ... def __fa(self): #在定义时就变形为_A__fa ... print('from A') ... def test(self): ... self.__fa() #只会与自己所在的类为准,即调用_A__fa ... >>> class B(A): ... def __fa(self): ... print('from B') ... >>> b=B() >>> b.test() from A

1.3 继承与抽象(先抽象在继承)

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

抽象分成两个层次:

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

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

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

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

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

例子:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

class Animal: def __init__(self,name,age): self.name=name self.age=age def walk(self): def say(self): print('%s is saying'%self.name) class People(Animal): pass class Pig(Animal): pass class Dog(Animal): pass p1=People("obama",50) print(p1.name) print(p1.age) p1.walk() #先找walk名字 找相应的值,去父类找 p1.say()

1 2 3 4

obama 50 obama is walking obama is saying

1.4 继承与重用性

在开发程序的过程中,如果我们定义了一个类A,然后又想新建立另外一个类B,但是类B的大部分内容与类A的相同时

我们不可能从头开始写一个类B,这就用到了类的继承的概念。

通过继承的方式新建类B,让B继承A,B会‘遗传’A的所有属性(数据属性和函数属性),实现代码重用

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38

class Hero: def __init__(self,nickname,aggressivity,life_value): self.nickname=nickname self.aggressivity=aggressivity self.life_value=life_value def move_forward(self): print('%s move forward' %self.nickname) def move_backward(self): print('%s move backward' %self.nickname) def move_left(self): print('%s move forward' %self.nickname) def move_right(self): print('%s move forward' %self.nickname) def attack(self,enemy): enemy.life_value-=self.aggressivity class Garen(Hero): pass class Riven(Hero): pass g1=Garen('草丛伦',100,300) r1=Riven('锐雯雯',57,200) print(g1.life_value) r1.attack(g1) print(g1.life_value) ''' 运行结果 300 243 '''

1.5 继承与派生

1、派生,父类里面没有的,在子类里面定义独有的,派生出新的东西。

2、派生出父类里面重名的东西,再找先找用子类自己的构造方法。

例子:父类里面没有的,在子类里面定义独有的

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

class Hero: def __init__(self,nicname,aggressivity,life_value): self.nicname=nicname self.aggressivity=aggressivity self.life_value=life_value def attack(self,enemy): enemy.life_value-=self.aggressivity class Garen(Hero): camp='Demacia' class Riven(Hero): camp='Noxus' g1=Garen('garen',18,200) r1=Riven('riven',18,200) print(g1.camp) print(r1.camp)

输出结果为:

1 2

Demacia Noxus

父类里面重名的东西,再找先找用子类自己的构造方法。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

class Hero: def __init__(self,nicname,aggressivity,life_value): self.nicname=nicname self.aggressivity=aggressivity self.life_value=life_value def attack(self,enemy): print('Hero attack') enemy.life_value-=self.aggressivity class Garen(Hero): camp='Demacia' def attack(self,enemy): Hero.attack(self,enemy) print('from garen attack') def fire(self): print('%s is firing'%self.nicname) class Riven(Hero): camp='Noxus' g1=Garen('garen',18,200) r1=Riven('riven',18,200) print(g1.camp) g1.attack(r1) print(g1.camp)

输出结果为:

1 2 3

Hero attack from garen attack Demacia

延续第二种:在子类里面添加新的独有的功能,如:“script”garen独有的,但是garen的身份在父类里,可以重用父类的功能

在子类中,重用父类的函数,父类名.父类函数(参数)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

class Hero: def __init__(self,nicname,aggressivity,life_value): self.nicname=nicname self.aggressivity=aggressivity self.life_value=life_value def attack(self,enemy): print('Hero attack') enemy.life_value-=self.aggressivity class Garen(Hero): camp='Demacia' def __init__(self,nicname,aggressivity,life_value,script): Hero.__init__(self,nicname,aggressivity,life_value) self.script=script def attack(self,enemy): Hero.attack(self,enemy) print('from garen attack') g1=Garen('garen',18,2,'人在塔在') print(g1.script)

输出结果为:

1

人在塔在

2、组合 

软件重用的重要方式除了继承之外还有另外一种方式,即:组合

组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合

组合对比继承来说,也是用来重用代码,但是组合描述的是一种“有的关系”

2.1 组合的方式:

用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如老师学生有python课程

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

class Teacher: def __init__(self,name,sex,course): self.name=name self.sex=sex self.course=course class Student: def __init__(self,name,sex,course): self.name=name self.sex=sex self.course=course class Course: def __init__(self,name,price,period): self.name=name self.price=price self.period=period Course_obj=Course('python','15800','7m') t1=Teacher('allen','male',Course_obj) s1=Student('susan','male',Course_obj) print(s1.course.name) print(t1.course.name)

输出结果为:

1 2

python python

组合与继承都是有效地利用已有类的资源的重要方式。但是二者的概念和使用场景皆不同 

2.2 继承的方式:

通过继承建立了派生类与基类之间的关系,它是一种'是'的关系,比如白马是马,人是动物。

当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好,比如教授是老师

1 2 3 4 5 6 7 8 9 10 11 12 13 14

>>> class Teacher: ... def __init__(self,name,gender): ... self.name=name ... self.gender=gender ... def teach(self): ... print('teaching') ... >>> >>> class Professor(Teacher): ... pass ... >>> p1=Professor('egon','male') >>> p1.teach() teaching

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018年12月25日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、 继承顺序
  • 二、 继承原理(python如何实现的继承)
  • 三、子类中调用父类方法(super()方法)
    • 1 为什么要封装
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档