python基础教程:类的继承
面向对象语言的一个特性就是类的继承。继承的关系跟人类繁衍的关系相似,被继承的类称为基类(也叫做父类),继承而得的类叫派生类(也叫子类),这种关系就像人类的父子关系。
类的继承,说明了不同类直接的关系,派生类复用了基类的代码同时也继承了基类的属性和方法。派生类定义的语法如下:
派生类的定义
class DerivedClassName(BaseClassName):
语句1
…
语句n
1
2
3
4
5
下面我们根据这个语法来写一个继承的例子:
class Person:
def init(self, name, age, height):
self.name = name
self.age = age
self.height = height
def look(self):
print(self.name, 'is looking')
def walk(self):
print(self.name, 'is walking')
class Teacher(Person):
def init(self, name, age, height):
super().init(name, age, height)
def teach(self):
print(self.name, 'is teaching')
class Student(Person):
def init(self, name, age, height):
super().init(name, age, height)
def learn(self):
print(self.name, 'is learning')
if name == ‘main’:
teacher = Teacher(‘Tom’, 31, 178)
s1 = Student(‘Jim’, 12, 160)
s2 = Student(‘Kim’, 13, 162)
teacher.look()
teacher.walk()
teacher.teach()
print('==='*5)
s1.look()
s1.walk()
s1.learn()
print('==='*5)
s2.look()
s2.walk()
s2.learn()
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
39
40
41
42
43
44
45
我们定义了一个基类Person,这个人有名字(name)、年龄(age)和身高(height)三个属性,还有look(), walk()两个方法。由它派生出两个类:Teacher和Student,这两个派生类继承了name, age, height属性,也继承了look(), walk()方法。但它们又有自己独有的方法,Teacher可以teach(),Student可以learn()。
运行这个示例代码,可以得到下面的信息:
Tom is looking
Tom is walking
Tom is teaching
Jim is looking
Jim is walking
Jim is learning
Kim is looking
Kim is walking
Kim is learning
1
2
3
4
5
6
7
8
9
10
11
12
派生类定义的执行过程与基类相同。 当构造类对象时,基类会被记住。 此信息将被用来解析属性引用:如果请求的属性在类中找不到,搜索将转往基类中进行查找。 如果基类本身也派生自其他某个类,则此规则将被递归地应用。
派生类的实例化会创建该类的一个新实例。方法引用将按以下方式解析:搜索相应的类属性,如果搜索的方法在派生类中找不到,就去基类中进行查找,如果基类本身也派生自其它类,则此规则将被递归地应用。如果搜索到了一个函数对象则方法引用就生效。
比如,Teacher中并没有定义look(),当我们调用方法teacher.look()的时候会在它的基类Person中找到并调用look()。
重载基类方法
派生类可以重载基类的方法。重载,就是重新定义。如果派生类中重新定义了基类的某方法,那么派生类的重载的这个方法就会覆盖基类中的同名方法。
例如,我们定义Student时可以重载walk()方法,让它拥有Student特征:
class Student(Person):
def init(self, name, age, height):
super().init(name, age, height)
def learn(self):
print(self.name, 'is learning')
def walk(self):
print('Student:', self.name, 'is walking')
1
2
3
4
5
6
7
8
9
10
再次运行s1.walk()时,就会打印下面的信息:
Student: Jim is walking
1
2
Python有两个内置函数可被用于检查继承机制:
isinstance()来检查一个实例的类型:isinstance(obj, int)仅仅会在obj.__class__为int或某个派生自int的类时为True。
issubclass()来检查类的继承关系:issubclass(bool, int)为True,因为bool是int的子类。但是,issubclass(float, int)为False,因为float不是int的子类。
多重继承
多重继承的意思就是,一个派生类同时派生自多个基类,继承它们全部属性和方法。它的定义形式是:
class DerivedClassName(Base1, Base2, Base3):
语句1
…
语句1
1
2
3
4
5
对于多数应用来说,在最简单的情况下,你可以认为搜索从父类所继承属性的操作是深度优先、从左至右的,当层次结构中存在重叠时不会在同一个类中搜索两次。 因此,如果某一属性在 DerivedClassName 中未找到,则会到 Base1 中搜索它,然后(递归地)到 Base1 的基类中搜索,如果在那里未找到,再到 Base2 中搜索,依此类推。
真实情况比这个更复杂一些;方法解析顺序会动态改变以支持对 super() 的协同调用。 这种方式在某些其他多重继承型语言中被称为后续方法调用,它比单继承型语言中的 super 调用更强大。
比如,我们要定义一个“助教”类,助教是帮助老师教学的高年级同学。他兼具老师和学生的特点,我们可以让这个类多重继承“老师类”和“学生类”。
私有变量
我们上一节讲过,Python中没有类似C++中的“私有变量”。但是,大多数Python代码都遵循这样一个约定(只是约定但很重要):带有一个下划线的名称(例如:_name)应当被动作是API的非仅供部分(无论它是函数、方法或是数据成员)。 这应当被视为一个实现细节,可能不经通知即加以改变。
由于存在对于类私有成员的有效使用场景(例如避免名称与子类所定义的名称相冲突),因此存在对此种机制的有限支持,称为名称改写。 任何形式为__name的标识符(至少带有两个前缀下划线,至多一个后缀下划线)的文本将被替换为_classname__name,其中classname为去除了前缀下划线的当前类名称。这种改写不考虑标识符的句法位置,只要它出现在类定义内部就会进行。
名称改写有助于让子类重载方法而不破坏类内方法调用。例如:
class MyList:
def init(self, iterable):
self.items_list = []
self.__update(iterable)
def update(self, iterable):
for item in iterable:
self.items_list.append(item)
__update = update # 把update()拷贝给私有方法
class MyListSubclass(MyList):
def update(self, keys, values):
# 重载 update()
# 但不会破坏 __init__()
for item in zip(keys, values):
self.items_list.append(item)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
这个示例中即使正在MyListSubclass引入一个__update标识符的情况下也不会出错,因为它会在MyList类中被替换为_MyList__update,而在MyListSubclass类中被替换为_MyListSubclass__update。
改写规则的设计主要是为了避免意外冲突;访问或修改被视为私有的变量仍然是可能的。这在特殊情况下甚至会很有用,例如在调试器中。
下一篇: Photoshop制作逼真的古典留声机