python--类的继承、多态
封装
封装是面向对象的特征之一,是对象和类概念的主要特性。
封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
继承
之前在类的实例化说到了类的公有属性和类的私有属性,其实就是类的封装,现在准备随笔的是继承,是面向对象的第二大特性。
面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
通过继承创建的新类称为“子类”或“派生类”,被继承的类称为“基类”、“父类”或“超类”,继承的过程,就是从一般到特殊的过程。在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。
继承概念的实现方式主要有2类:实现继承、接口继承。
- 实现继承是指使用基类的属性和方法而无需额外编码的能力。
- 接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力(子类重构爹类方法)。
在考虑使用继承时,有一点需要注意,那就是两个类之间的关系应该是“属于”关系。例如,Employee 是一个人,Manager也是一个人,因此这两个类都可以继承 Person类。但是 Leg类却不能继承Person类,因为腿并不是一个人。
抽象类仅定义将由子类创建的一般属性和方法,OO开发范式大致为:划分对象→抽象类→将类组织成为层次化结构(继承和合成) →用类与实例进行设计和实现几个阶段
类的继承
1、继承的定义
在类名的括号中写入需要继承的类名即可
class Person(object): def talk(self): print("person is talking...") class BlackPerson(Person): #继承Person这个类 def walk(self): #定义子类自身的walk方法 print("BlackPerson is walking.....") b = BlackPerson() b.talk() #由于继承了父类的talk()方法,所以可以被调用 b.walk() #调用子类自身的walk方法 #输出 person is talking... BlackPerson is walking.....
2、构造方法的继承
说明:因为子类有自己的属性,但是又想继承父类的属性,所以需要先继承,再重构
继承类的构造方法2种写法:
- 经典类写法:父类.__init(self,name,age)
- 新式类写法:super(子类,self).__init__(name,age)
注:建议使用新式类的写法,因为使用经典类的写法,在多继承的情况下,会出现重复调用参数的可能
class Person(object): def __init__(self, name, age): self.name = name self.age = age self.sex = "noraml" def talk(self): print("person is talking...") class BlackPerson(Person): def __init__(self, name, age, strength): # 定义时需要传入父类的属性名 Person.__init__(self, name, age) # 继承父类的构造方法,也可以写成:super(BlackPerson,self).__init__(name,age) self.strength = strength # 定义子类本身的属性 print(self.name, self.age, self.sex) def walk(self): print("BlackPerson is walking.....") b = BlackPerson("xiaogao", 18, "strong") # 输出结果 xiaogao 18 noraml
这边不禁的有一个疑问?我不能重新写一遍,我为啥要继承父类中的方法呢?因为你重新写一遍的话,只能继承self.name和self.age,那self.sex怎么办,它也需要重写吗?所以啊,只有把父类中的构造函数全部继承过来,只能用上面这种办法?那它是怎么实现的呢?我们来画一个图:
3、子类对父类方法的重写
说明:如果我对父类中的方法不满意,我可以重写父类中的方法,当然还可以继承父类中的方法
class Person(object): def __init__(self, name, age): self.name = name self.age = age self.sex = "noraml" def talk(self): print("person is talking...") class BlackPerson(Person): def talk(self): # 重写父类的方法 Person.talk(self) # 调用父类的方法 print("BlackPerson is talking ...") def walk(self): print("BlackPerson is walking.....") b = BlackPerson("xiaogao", 18) # 子类不写,则继承父类的构造方法 b.talk() b.walk() #输出结果 person is talking... BlackPerson is talking ... BlackPerson is walking.....
其实重写不是上面说的那么简单,只需要子类的方法名和父类的中的方法名一样就算重写了,其实不然,那怎么样才算重写呢?
重写的条件:
- 重写方法的方法名必须和父类中被重写的方法名一模一样
- 重写方法的传入的参数名和参数的个数必须和父类中被重写的方法一样
我们再来看看下面的例子:
class Person(object): def talk(self,food): print("person is talking...{0}".format(food)) class BlackPerson(Person): def talk(self): #方法名和父类的方法名一样,但是少了一个food参数 print("BlackPerson is talking ...") b = BlackPerson("xiaogao",18,"strong") b.talk() #输出 BlackPerson is talking ...
看着像重写了呀!其实不是重写,根据重写的条件明显两个方法的传入参数名和参数的个数都不一样,其实上面这种只是子类自己写了一个talk方法,只是名字一样,但是传入的参数和参数的个数不一样,并不是重写了父类中的方法。下面这个才是真正的重写:
class Person(object): def talk(self,food): print("person is talking...{0}".format(food)) class BlackPerson(Person): def talk(self,food): #重写父类的方法(方法名和传入的参数名以及参数的个数与父类的方法一样) print("BlackPerson is talking ...{0}".format(food)) b = BlackPerson("xiaogao",18,"strong") b.talk("hotdog") #输出 BlackPerson is talking ...hotdog
类继承练习的小例子:
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 enroll a new school member [{0}]".format(self.name)) SchoolMember.member += 1 def tell(self): print("------info:{0}-----".format(self.name)) for k,v in self.__dict__.items(): #__dict__()函数是获取对象的属性,以字典的形式返回 print("\t",k,v) print("------end--------") def __del__(self): print("开除了[{0}]...".format(self.name)) SchoolMember.member -= 1 class Teacher(SchoolMember): "讲师类" def __init__(self,name,age,sex,salary,course): SchoolMember.__init__(self,name,age,sex) self.salary = salary self.course = course def teaching(self): "讲课方法" print("Teacher [{0}] is teaching [{1}]".format(self.name,self.course)) class Student(SchoolMember): "学生类" def __init__(self,name,age,sex,couser,tuition): SchoolMember.__init__(self,name,age,sex) self.couser = couser self.tuition = tuition self.amount = 0 def pay_tuition(self,amount): print("student [{0}] has just paied [{1}]".format(self.name,amount)) self.amount += amount t1 = Teacher("xiaogao",18,"F*M",3000,"Python") s1 = Student("shuaigao",19,"M","PYS15",300000) s2 = Student("gaogao",12,"M","PYS15",11000) print(SchoolMember.member) del s1 #删除一个变量 t1.tell() s2.tell() print(SchoolMember.member) #会执行__del__函数 #输出 just enroll a new school member [xiaogao] just enroll a new school member [shuaigao] just enroll a new school member [gaogao] 3 开除了[shuaigao]... ------info:xiaogao----- salary 3000 sex F*M course Python name xiaogao age 18 ------end-------- ------info:gaogao----- couser PYS15 sex M name gaogao amount 0 tuition 11000 age 12 ------end-------- 2 开除了[gaogao]... 开除了[xiaogao]...
4、多继承
上面的都是单继承,那么什么是多继承呢?说白了,就是:子类可以继承多个父类,就叫多继承。
class SchoolMember(object): #SchoolMember类 '''学校成员基类''' def tell(self): print("the schoolmeber is tell...") class School(object): #School类 """学校类""" def open_branch(self,addr): print("openning a new branch in",addr) class Teacher(SchoolMember,School): #子类Teacher同时继承了SchoolMember,School两个类 "讲师类" def teaching(self): "讲课方法" print("Teacher xiaogao is teaching python") t1 = Teacher() t1.tell() #拥有父类SchoolMember的tell方法 t1.open_branch("shanghai") #拥有父类School的open_branch方法
5、新式类
概念
新式类定义时必须继承object类,被定义继承了object类的,就叫做新式类
class Person(object): #继承object类 "新式类"
继承构造方法
新式类初始化构造方法用super关键字去继承
super(子类,self).__init__(name,age)
调用父类中相同方法或者相同属性的顺序
新式类多继承的调用方法是顺序是:广度优先查询,如下图:
代码实验如下:
①全部代码
class A(object): #新式类 def __init__(self): self.n = "A" class B(A): def __init__(self): self.n = "B" class C(A): def __init__(self): self.n = "C" class D(B,C): def __init__(self): self.n = "D" d = D() print(d.n) #输出 D
先找自己本身类的属性,输出D
②注释D类中代码
class A: def __init__(self): self.n = "A" class B(A): def __init__(self): self.n = "B" class C(A): def __init__(self): self.n = "C" class D(B,C): pass d = D() print(d.n) #输出 B
③注释B类中的代码
class A: def __init__(self): self.n = "A" class B(A): pass class C(A): def __init__(self): self.n = "C" class D(B,C): pass d = D() print(d.n) #输出 A
④注释C类中的代码
class A(object): def __init__(self): self.n = "A" class B(A): pass class C(A): pass class D(B,C): pass d = D() print(d.n) #输出 A
6、经典类
概念
经典类定义,什么都不继承
class Person: "经典类"
继承构造方法
父类.__init(self,name,age)
调用父类中相同方法或者相同属性的顺序
经典类多继承的调用方法是顺序是:深度优先查询,如下图:
代码实验如下:
①全部代码
class A:#经典类 def __init__(self): self.n = "A" class B(A): pass def __init__(self): self.n = "B" class C(A): def __init__(self): self.n = "C" class D(B,C): def __init__(self): self.n = "D" d = D() print(d.n) #输出 D
②注释D类中代码
class A: def __init__(self): self.n = "A" class B(A): def __init__(self): self.n = "B" class C(A): def __init__(self): self.n = "C" class D(B,C): pass d = D() print(d.n) #输出 B
③注释B类中的代码
class A: def __init__(self): self.n = "A" class B(A): pass class C(A): def __init__(self): self.n = "C" class D(B,C): pass d = D() print(d.n) #输出 A
④注释A类中的代码
class A: pass class B(A): pass class C(A): def __init__(self): self.n = "C" class D(B,C): pass d = D() print(d.n) #输出 C
总结:
- 新式类继承object类,经典类不继承任何类
- 新式类用super关键字继承构造方法,经典类用 父类.__init(self)来继承
- 新式类:广度优先查询,经典类:深度优先查询(因为新式类讲究的是新,所以要找最近的,最新的;然后经典的讲究古老,所以更远更深的)
- 值得注意的是,我们上面是在python2中做的,在python3中不管是经典类还是新式类,都是采用的是广度优先查询,已经废弃2中的深度查询了
多态
多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。
#_*_coding:utf-8_*_ class Animal(object): def __init__(self, name): # Constructor of the class self.name = name def talk(self): # Abstract method, defined by convention only raise NotImplementedError("Subclass must implement abstract method") class Cat(Animal): def talk(self): print('%s: 喵喵喵!' %self.name) class Dog(Animal): def talk(self): print('%s: 汪!汪!汪!' %self.name) def func(obj): #一个接口,多种形态 obj.talk() c1 = Cat('小晴') d1 = Dog('李磊') func(c1) func(d1)