python编写代码定义一个形状基类_Python的程序结构[2] -> 类/Class[1] -> 基类与继承...

基类与继承/ Base Class and Inheritance Class

面向对象的特性使得 Python 中不可避免地需要使用到类和类的继承,类的继承可以使得代码很好的被重用。下面以一些代码示例说明类的继承如何使用。

继承一个基类

首先,定义一个基类 Animal,在初始化中设定一个基本属性以及物种信息,并设置其具有 eat 的能力(self.eat 为 True)。此处还重载了魔术方法 __getattr__,当搜索的属性不存在时返回 False(即不具备该能力),最后定义一个 show 函数来显示当前 species 信息。

1 #------- Basic inheritance --------

2 classAnimal:3 def __init__(self):4 self.base = "Creature"

5 self.species = "Animal"

6 self.eat =True7

8 def __getattr__(self, item):9 returnFalse10

11 defshow(self):12 print("This is %s." % self.species)

接着定义一个 Bird 类,继承自 Animal ,在 Bird 的初始化函数中,首先对基类进行初始化,然后设置 species 属性,以及 fly 为True(代表具有 fly 的能力),并对 Bird 定义了 show 函数。同样定义 Fish 类,设置 species 信息以及 swim 为 True,且不定义 show 函数。

1 #Note: Do not mix super and Object.__init__

2 classBird(Animal):3 def __init__(self):4 Animal.__init__(self)5 self.species = "Bird"

6 self.fly =True7

8 defshow(self):9 print("This is %s from %s." %(self.species, self.base))10

11 classFish(Animal):12 def __init__(self):13 Animal.__init__(self)14 self.species = "Fish"

15 self.swim = True

最后,对两个类分别进行实例化,并调用各自的 show 函数,

1 b =Bird()2 f =Fish()3 b.show()4 f.show()

输出的结果如下,

This is Bird fromCreature.

Thisis Fish.

从最终的输出结果可以看到,

对于 Bird 类,并没有对 base 属性进行定义,但却拥有 base 属性,即这一属性从基类 Animal 中继承得到,

而对于 Fish 类,并未定义 show 方法,但是却在实例中可以使用 show 方法。同样这一方法也是继承自基类 Animal。

这两个简单的示例中通过继承从而避免了重复的代码和定义。

多继承

前面的例子中使用的基类是唯一的,当需要从多个基类中继承时,则会涉及到多继承问题。下面的代码给出一个多继承问题的示例。

首先导入之前定义的两个类 Bird 和 Fish 作为基类,然后基于这两个类派生出子类 Duck 和 Goose,其中 Goose 中重载了初始化函数,利用 super初始化 Goose 的基类。此时,我们希望 Duck 和 Goose 能够继承 Bird 和 Fish 的两个特性,fly 和 swim。

Note: 此处为示例代码,切勿混用 super 和类的初始化函数。

1 from inheritance_base importBird, Fish2 #------- Multi inheritance --------

3 class Duck(Bird, Fish): pass

4

5 classGoose(Bird, Fish):6 def __init__(self):7 super(Goose, self).__init__()8

9 d =Duck()10 g =Goose()11 print("I am %s, I can fly: %s" %(d.species, d.fly))12 print("I am %s, I can swim: %s" %(d.species, d.swim))13 print("I am %s, I can fly: %s" %(g.species, g.fly))14 print("I am %s, I can swim: %s" % (g.species, g.swim))

最终的运行结果如下,

I am Bird, I can fly: True

I am Bird, I can swim: False

I am Bird, I can fly: True

I am Bird, I can swim: False

从运行的结果中看到,最后的输出却不是我们所期望的,无论是 Duck 的实例还是 Goose 的实例都不具备 swim 的属性,也就是说,Fish 的初始化并未被执行。

其原因在于,Duck 中并未定义初始化函数,因此会在父类中搜索,从而调用到 Bird 的初始化函数,所以最终显示 species 属性为 Bird,而 swim 为 False。而在 Goose 中,使用 super 也未能成功继承所有属性,这是由于 super 会依照 MRO 顺序进行搜索,使用搜索到的第一个类的对应方法。

为实现所需要的功能,在这里重新定义一个 Duck 类,重载初始化函数,在初始化函数中分别调用两个基类的初始化函数。

1 classDuck(Bird, Fish):2 def __init__(self):3 Bird.__init__(self)4 Fish.__init__(self)5

6 d =Duck()7 print("I am %s, I can fly: %s" %(d.species, d.fly))8 print("I am %s, I can swim: %s" % (d.species, d.swim))

最终输出结果可以看到,species 属性被覆盖了最终显示为 Fish,而 Goose 也具备了 fly 和 swim 两个属性。

I am Fish, I can fly: True

I am Fish, I can swim: True

但这种继承依旧会引起一些问题。

菱形/钻石继承

前面的多继承使用直接调用父类初始化函数进行,表面上能够满足多继承的需求,但是仍然存在一些问题,下面以一个菱形/钻石继承来说明这一问题是如何存在的。

首先是定义 A、B、C、D 四个类,这四个类大致为菱形继承的形式。

"""A

/\

/ \

/ \

B C

\ /

\ /

\/

D"""

然后以 no-super 和 super 两种方式来实现这一继承,

在 no super 中不使用 super,直接使用调用父类初始化函数的方法来完成初始化工作,具体方式如下,

1 #------- no super ----------

2 classA(object):3 def __init__(self):4 object.__init__(self)5 print("This is A init.")6

7 classB(A):8 def __init__(self):9 A.__init__(self)10 print("This is B init.")11

12 classC(A):13 def __init__(self):14 A.__init__(self)15 print("This is C init.")16

17 classD(B, C):18 def __init__(self):19 B.__init__(self)20 C.__init__(self)21 print("This is D init.")22

23 d = D()

输出结果如下,从输出中可以看出,这种方式存在一个问题,那就是对 B 和 C 的基类 A 进行了多次的初始化。这可能会造成一些不期望的结果。

This isA init.

ThisisB init.

ThisisA init.

ThisisC init.

Thisis D init.

为了避免这种现象,可以使用 super 来完成继承初始化。

1 #------- super ------------

2 classA(object):3 def __init__(self):4 super(A, self).__init__()5 print("This is A init.")6

7 classB(A):8 def __init__(self):9 super(B, self).__init__()10 print("This is B init.")11

12 classC(A):13 def __init__(self):14 super(C, self).__init__()15 print("This is C init.")16

17 classD(B, C):18 def __init__(self):19 super(D, self).__init__()20 print("This is D init.")21

22 d = D()

使用super的初始化结果显示如下,可以看到,此时能够实现对 A 的初始化只进行一次。这是由于 super 中实现了 MRO 的搜索算法。

This isA init.

ThisisC init.

ThisisB init.

Thisis D init.

相关阅读

1.声明一个动物基类Animal,私有整型成员变量年龄age,请定义一个派生Dog,在其成员函数SetAge(int n)中直接给age赋值,测试下看是否会出问题?如何解决? 2.设计一个单基继承层次程序,用Person派生出Student,增加属性学号index和年级level。Person中至少有姓名name、年龄age等数据成员,以及构造函数、输出函数等,其余成员函数根据需要添加。在主函数中进行测试。 3.定义一个学生Student和教师Teacher,学生有姓名name、学号index等数据成员,教师有姓名name、工作证号workID、职称title、课程course、周学时hoursPerWeek等数据成员。再定义一个助教TeachingAssistant,多继承于学生和教师,该可以使用学生的全部数据成员,以及教师的课程和周学时数据成员。要求:每个提供自定义的构造函数和析构函数,并通过同名函数ShowInfo来显示全部数据成员的值。在主函数中进行测试。 4.声明一个Person,包含姓名name和年龄age等私有数据成员以及相关的成员函数;由它派生出领导Leader,包含职务position和部门department私有数据成员以及相关的成员函数;再由Person派生出工程师Engineer,包含职务position和专业speciality私有数据成员以及相关的成员函数;再由Leader和Engineer派生出主任工程师Chairman。在主函数中测试各对象初始化和信息输出,查看是否会出问题?如何解决?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值