类的继承实例
继承
面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
通过继承创建的新类称为“子类”或“派生类”。
被继承的类称为“基类”、“父类”或“超类”。
继承的过程,就是从一般到特殊的过程。
要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。
在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。
继承概念的实现方式主要有2类:实现继承、接口继承。
OO开发范式大致为:划分对象→抽象类→将类组织成为层次化结构(继承和合成) →用类与实例进行设计和实现几个阶段。
下面我们来看个继承的简单实例:
如果子类有一个跟父类一样的方法:
我们现在给父类增加两个参数,name,age。
这个时候调用子类用方法b=BlackPerson(),必须带上参数name和age,否则就会报错。如下图:
子类调用必须加上参数如下图:
如果我想给子类传参数,不可以在子类下面增加初始化函数。
我们应该先继承再重构函数:
甚至在子类里可以自定义新的属性了,如下图:
现在我们来看一个完整的学校,老师和学生的例子:
class SchoolMember(object): '''学校成员基类''' member = 0 def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex self.enroll() def enroll(self): '''注册''' print("just enrolled a new school member [%s]"% self.name) SchoolMember.member +=1 def tell(self): print("------info:%s-----"%self.name) for k,v in self.__dict__.items(): print(" ",k,v) print("------end-----") def __del__(self): print("开除了[%s]..."% self.name ) SchoolMember.member -=1 class School(object): '''学校类''' def open_branch(self,addr): print("openning a new branch in ",addr) class Teacher(SchoolMember,School): '''讲师类''' def __init__(self,name,age,sex,salary,course): #SchoolMember.__init__(self,name,age,sex) #经典类写法 super(Teacher,self).__init__(name,age,sex) #新式类写法 self.salary = salary self.course = course # def tell(self): # print("""--------info:%s # name:%s # age:%s # salary :%s # """ % (self.name,self.name,self.age,self.salary)) def teaching(self): print("Teacher [%s] is teaching [%s]" % (self.name,self.course)) class Student(SchoolMember): def __init__(self,name,age,sex,course,tuition): SchoolMember.__init__(self,name,age,sex) self.course = course self.tuition = tuition #fee self.amount = 0 def pay_tuition(self,amount): print("student [%s] has just paied [%s]" %(self.name,amount)) self.amount += amount t1 = Teacher("Wusir", 28, "F*M",3000,"Python" ) s1 = Student("HaiTao", 38,"N/A","PYS15", 300000 ) s2 = Student("LIChuang",12,"M","PYS15", 11000 ) print(SchoolMember.member) t1.tell() t1.open_branch("SH") s2.tell() del s2 print(SchoolMember.member)
上面的例子,几个注意要点:
这必须用类名SchoolMember而不能用self,如果用self就没法实现全局变量进行累加的效果了~!
我们如果想打印老师和学生,实例的所有信息,怎么便捷的操作呢?
我们发现实例后面.__dict__可以用字典的方式把所有的信息打印出来:
那么我们可以在父类直接加上循环打印实例信息。
在上面代码里,还有一个多继承的问题,也就是老师其实继承了2个父类,一个SchoolMember一个是School。
最后我们来看下继承父类有两个写法,新式类VS经典类:
其实新式类的定义class Person(object),这个object也是一个系统默认的类,那么为什么python要让所有的类的父类都是object呢?
因为万一以后python想给类class加什么新功能,它直接加在class object的方法就可以,这样就可以无限扩展了!所以推荐用新式类的写法来给类定义。
现在我们再来看一个在经典模式和新式模式下,构建函数,初始化的查找顺序问题:
在默认的情况下,见下面的代码:
class A(object): def __init__(self): self.n = "A" class B(A): # pass def __init__(self): self.n = "B" class C(A): #pass def __init__(self): self.n = "C" class D(B,C): # pass def __init__(self): self.n = "D" d = D() print(d.n)
打印出来的结果肯定是D,这个时候搜索的最近的初始化。
如果我们把D的初始化注释掉:
这个时候打印出来的结果是B;
同样我们把B注释掉,打印出来的是C;
再把C注释掉,打印出来的是A。
我们来看下面的关系图:
查找的顺序是D->B->C->A,也就是在PY3里,查找的顺序是按广度查询的方式,先在同级的class查找,然后再按深度查询的方法,查找到A。
同样在经典模式的情况下,也就是把A的object去掉:
这个时候在PY3里查找的顺序依旧是D->B->C->A。
但是注意,有特殊的情况,如果在PY2里,经典模式的查找顺序就是D->B->A->C,在PY2里查找的顺序优先是深度查找,然后才是广度查询!