python面向对象之继承与派生

一、 继承顺序

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 查看继承 

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

猜你喜欢

转载自blog.csdn.net/bbwangj/article/details/85213759