0%

Python面向对象—继承和多态

类的继承

继承可以复用已有代码,自动拥有现有类的所有功能,只需编写缺少的新功能。

如果已经定义了Person类,需要定义新的Student和Teacher类时,可以直接从Person类继承,定义Student类时,只需要把额外的属性加上,

1
2
3
4
5
6
7
8
9
class Person(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender

class Student(Person):
def __init__(self, name, gender, score):
super(Student, self).__init__(name, gender)
self.score = score

一定要用super(Student, self).__init__(name, gender)去初始化父类,否则,继承自Person的Student将没有name和gender
函数super(Student, self)将返回当前类继承的父类,即Person,然后调用__init__()方法,注意self参数已在super()中传入,在__init__()中将隐式传递,不需要写出(也不能写)

多态

类具有继承关系,并且子类类型可以向上转型看做父类类型。如果从Person派生出Student和Teacher,并都拥有whoAmI()方法如下,那么在该函数中,如果接收一个变量x,则无论该x是Person、Student还是Teacher,都可以正确打印出结果,

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
class Person(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender
def whoAmI(self):
return 'I am a Person, my name is %s' % self.name

class Student(Person):
def __init__(self, name, gender, score):
super(Student, self).__init__(name, gender)
self.score = score
def whoAmI(self):
return 'I am a Student, my name is %s' % self.name

class Teacher(Person):
def __init__(self, name, gender, course):
super(Teacher, self).__init__(name, gender)
self.course = course
def whoAmI(self):
return 'I am a Teacher, my name is %s' % self.name

def who_am_i(x):
print x.whoAmI()

p = Person('Tim', 'Male')
s = Student('Bob', 'Male', 88)
t = Teacher('Alice', 'Female', 'English')

who_am_i(p)
who_am_i(s)
who_am_i(t)

#运行结果:
I am a Person, my name is Tim
I am a Student, my name is Bob
I am a Teacher, my name is Alice

这种行为称为多态。也就是说,方法调用将作用在x的实际类型上。s是Student类型,它实际上拥有自己的whoAmI()方法以及从Person继承的whoAmI()方法,但调用s.whoAmI()总是先查找它自身的定义,如果没有定义,则顺着继承链向上查找,直到在某个父类中找到为止。
由于Python是动态语言,所以,传递给函数who_am_i(x)的参数x不一定是Person或Person的子类型。任何数据类型的实例都可以,只要它有一个whoAmI()的方法即可。
这是动态语言和静态语言(例如Java)最大的差别之一。动态语言调用实例方法,不检查类型,只要方法存在,参数正确,就可以调用

多重继承

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
class A(object):
def __init__(self, a):
print('init A...')
self.a = a

class B(A):
def __init__(self, a):
super(B, self).__init__(a)
print('init B...')

class C(A):
def __init__(self, a):
super(C, self).__init__(a)
print('init C...')

class D(B, C):
def __init__(self, a):
super(D, self).__init__(a)
print('init D...')

d = D('d')
#结果
init A...
init C...
init B...
init D...

如上,D同时继承自B和C,D就拥有了A、B、C的全部功能。多重继承通过super()调用__init__()方法时,A虽然被继承了两次,但__init__()只调用一次。

多重继承的目的是从两种继承树中分别选择并继承出子类,以便组合功能使用。举个例子,Python的网络服务器有TCPServer、UDPServer、UnixStreamServer、UnixDatagramServer,而服务器运行模式有多进程ForkingMixin和多线程ThreadingMixin两种。要创建多进程模式的TCPServer和多线程模式的UDPServer分别如下,

1
2
3
4
5
class MyTCPServer(TCPServer, ForkingMixin):
pass

class MyUDPServer(UDPServer, ThreadingMixin):
pass

如果没有多重继承,要实现上述所有可能的组合需要4x2=8个子类。

判断变量类型

函数isinstance()可以判断一个变量的类型,既可以用在Python内置的数据类型如str、list、dict,也可以用在自定义的类(它们本质上都是数据类型)。
假设有如下的Person、Student和Teacher的定义及继承关系如下,当拿到变量p、s、t时,可以使用isinstance判断类型,

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
class Person(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender

class Student(Person):
def __init__(self, name, gender, score):
super(Student, self).__init__(name, gender)
self.score = score

class Teacher(Person):
def __init__(self, name, gender, course):
super(Teacher, self).__init__(name, gender)
self.course = course

p = Person('Tim', 'Male')
s = Student('Bob', 'Male', 88)
t = Teacher('Alice', 'Female', 'English')

# 考察p
print(isinstance(p, Person)) #True p是Person类型
print(isinstance(p, Student)) # False p不是Student类型
print(isinstance(p, Teacher)) # False p不是Teacher类型

# 再考察s:
print(isinstance(s, Person)) # True s是Person类型
print(isinstance(s, Student)) # True s是Student类型
print(isinstance(s, Teacher)) # False s不是Teacher类型

对p的考察说明在继承链上,一个父类的实例不能是子类类型,因为子类比父类多了一些属性和方法
考察s时,s是Student类型,不是Teacher类型。s也是Person类型,因为Student继承自Person,拥有Person的所有公开的属性和方法。这说明在一条继承链上,一个实例可以看成它本身的类型,也可以看成它父类的类型

获取对象信息

1
2
3
4
5
6
7
8
9
10
11
class Person(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender

class Student(Person):
def __init__(self, name, gender, score):
super(Student, self).__init__(name, gender)
self.score = score
def whoAmI(self):
return 'I am a Student, my name is %s' % self.name

type()函数获取变量的类型,它返回一个Type对象;用dir()函数获取变量的所有属性

1
2
3
4
5
6
7
8
9
10
print(type(123))  # <type 'int'>
s = Student('Bob', 'Male', 88)
print(type(s)) # <class '__main__.Student'>
print(dir(123)) # 整数也有很多属性... # ['__abs__', '__add__', '__and__', '__class__', '__cmp__', ...]
print(dir(s))
['__class__', '__delattr__', '__dict__', '__doc__', '__format__',
'__getattribute__', '__hash__', '__init__', '__module__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__', 'gender', 'name', 'score',
'whoAmI']

对于实例变量,dir()返回所有实例属性,包括'__class__'这类有特殊意义的属性。注意到方法'whoAmI'也是s的一个属性。
dir()返回的属性是字符串列表,如果已知一个属性名称,要获取或者设置对象的属性,就需要用getattr()和setattr()函数,

1
2
3
4
5
6
7
8
9
10
11
12
# 获取name属性
print(getattr(s, 'name')) # 'Bob'
# 设置新的name属性
setattr(s, 'name', 'Adam')
print(s.name) # 'Adam'
# 获取age属性,但是属性不存在,报错
print(getattr(s, 'age'))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'age'
# 获取age属性,如果属性不存在,就返回默认值20
print(getattr(s, 'age', 20)) # 20
天生我材必有用,千金散尽还复来~
  • 本文作者: XTLei
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
-------------本文结束感谢您的阅读-------------