类的继承 继承可以复用已有代码,自动拥有现有类的所有功能,只需编写缺少的新功能。
如果已经定义了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' ) print(isinstance(p, Person)) print(isinstance(p, Student)) print(isinstance(p, Teacher)) print(isinstance(s, Person)) print(isinstance(s, Student)) print(isinstance(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 )) s = Student('Bob' , 'Male' , 88 ) print(type(s)) print(dir(123 )) 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 print(getattr(s, 'name' )) setattr(s, 'name' , 'Adam' ) print(s.name) print(getattr(s, 'age' )) Traceback (most recent call last): File "<stdin>" , line 1 , in <module> AttributeError: 'Student' object has no attribute 'age' print(getattr(s, 'age' , 20 ))