<h1 id="一-初识继承">一 初识继承
<h3 id="一-什么是继承">一 什么是继承?
继承是一种创建新类的方式,新建的类可以继承一个或多个父类(python支持多继承),父类又可成为基类或超类,新建的类称为派生类或子类.
python中类的继承分为:单继承和多继承
class Parent1: passclass Parent2:
passclass Sub1(Parent1): # 单继承,基类是Parent1,派生类是Sub
passclass Sub2(Parent1,Parent2): # 多继承,用逗号分隔开多个继承的类
pass
查看继承
print(sub2.__bases__) # __base__只查看继承的第一个子类,__bases__则是查看所有继承的父类
经典类与新式类
- 只有在python2中才分新式类和经典类,python3统一都是新式类
- 在python2中,没有显式的继承object类的类,类与该类的子类,都是经典类
- 在python2中,显式的声明继承object的类,都是新式类
- 在python3中,无论是否继承object,都默认继承object,即python3中所有的类均为新式类
python3中如果没有指定基类,python的类会默认继承object类,object类是所有python类的基类,它内置了一些方法.
继承描述的是子类与父类的继承关系,想要找出这种关系,必须先抽象再继承.
抽象:即抽取类似或者相似的部分
继承:是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构
在开发程序的过程中,如果我们首先定义了一个A类,然后又想新建一个B类,但是B类的大部分内容与A类代码相同时,可以通过继承的方法来实现代码重用.
B继承A会获得A的所有属性(数据属性与函数属性),所以B中没有而A中具有的属性可以被B用来使用,而B中有的就使用自己的:
class A: a = 123 print(a)def func1(self): print('A,func1') def func2(self): print('A,func2') self.func1()
class B(A):
a = 134
def func1(self):
print('B,func1')b = B()
print(b.a)
b.func2()运行结果
<h3 id="四-派生类">四 派生类
虽然子类是继承了父类的的属性,但是子类也可以添加自己新的属性或者对子类生成的对象定义自己的属性(不会影响到父类),需要注意的是,一旦重新定义了自己的属性且与父类同名时,那么调用新的属性时以子类的为准.
class A: a = 123 print(a)def func1(self): print('A,func1') def func3(self): print('B,func3')</code></pre>
软件重用的重要方式除了继承之外还有另外一种方式,即:组合
组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合
class Equip: #武器装备类 def fire(self): print('release Fire skill')class Riven: #英雄Riven的类,一个英雄需要有装备,因而需要组合Equip类
camp='Noxus'
def init(self,nickname):
self.nickname=nickname
self.equip=Equip() #用Equip类产生一个装备,赋值给实例的equip属性
r1=Riven('锐雯雯')
r1.equip.fire() #可以使用组合的类产生的对象所持有的方法运行结果
release Fire skill
组合与继承都是有效的利用已有类的资源的重要方式.但是二者的概念和使用场景皆不同.
- 继承的方式
通过继承建立了派生类和父类之间的关系,它是一种从属关系.
当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好
- 组合的方式
用组合的方式建立了类与组合的类之间的关系,它是一种附带关系.
- 什么是抽象类?
与java一样,python也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化.
- 为什么要有抽象类?
如果说类是从一堆对象中抽取相同的内容得来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性.
从设计角度看,抽象类与普通类的不同之处在于:抽象类中只能抽象方法(没有实现功能),该类不能被实例化,只能被继承,且子类必须实现抽象方法.
在python中可以同时继承多个父类,如A(B,C,D)
如果继承关系为非菱形结构,则会按照先找B这一条分支,然后再找C这一条分支,最后找D这一条分支的顺序直到找到要找的属性,找完了还没有的话会报属性不存在的错误.
如果继承关系为菱形结构,那么属性的查找方式有两种,分别是:深度优先和广度优先.
class A(object): def test(self): print('from A')class B(A):
def test(self):
print('from B')class C(A):
def test(self):
print('from C')class D(B):
def test(self):
print('from D')class E(C):
def test(self):
print('from E')class F(D,E):
def test(self):
# print('from F') pass
f1=F()
f1.test()
print(F.mro) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性新式类继承顺序:F->D->B->E->C->A
经典类继承顺序:F->D->B->A->E->C
python3中统一都是新式类
pyhon2中才分新式类与经典类
<h3 id="二-继承原理">二 继承原理
python是如何实现继承并查找属性的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表.
为了实现继承,python会在MRO列表中从左到右开始查找基类,直到找到第一个匹配这个属性的类为止.而这个MRO列表的构造是通过一个C3线性化算法来实现的.它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
查找属性时,即使没有直接继承关系,也会按照MRO列表继续往后查找,当使用super()函数时,python会在MRO列表上继续搜索下一个类.只要每个重定义的方法统一使用super()并只调用它一次,那么控制流最终会遍历完整个MRO列表,每个方法也只会被调用一次
注意:使用super调用的所有属性,都是从MRO列表当前的位置往后找,不要在意继承关系,看MRO列表就好
BaseTher类的创建
# coding: utf-8 # @Time : 2018/10/23 11:40 PM # @Author : MUSIBII # @Email : shaozuanzuan@gmail.com # @File : father_type.pyimport os,sys
import pickle
import hashlibsys.path.append(os.path.dirname(os.path.dirname(file)))
import db_file'''
11、抽象老师类与学生类得到父类,用继承的方式减少代码冗余'''
import time
class BaseTher:
def __init__(self,name,age,gender): self.name = name self.age = age self.gender = gender self.time = time.time() # self.id_hash = self.create_id() # 创建唯一的id def create_id(self): # timenow = str(time.time()).split('.')[0] key = self.name + str(self.age) + self.gender + str(self.time) m = hashlib.md5(key.encode('utf-8')) self.id_hash = m.hexdigest() # print(m.hexdigest()) # return m.hexdigest() def tell_info(self): data = self.get_obj_by_id() for item in data: print('%-10s: %-20s' % (item,data[item])) # print(data) # 将信息序列化后保存到<a href="/tag/wenjian/" target="_blank" class="keywords">文件</a> def save(self): user_data = {'name': self.name,'age': self.age,'gender': self.gender,'id_hash': self.id_hash} with open('db_file/%s' % self.id_hash,'wb') as f: pickle.dump(user_data,f) # 通过id反序列化信息 def get_obj_by_id(self): with open('db_file/%s' % self.id_hash,'rb') as f: return pickle.load(f)
子类通过super().__init__(*args)使用父类的__init__方法
class Teacher(BaseTher): def __init__(self,gender,level,salary): super().__init__(name,gender) self.level = level self.salary = salary # self.id_hash = self.create_id()# 创建唯一的id def create_id(self): # timenow = str(time.time()).split('.')[0] key = self.name + str(self.age) + self.gender + str(self.level) + str(self.salary) m = hashlib.md5(key.encode('utf-8')) self.id_hash = m.hexdigest() # print(m.hexdigest()) # return m.hexdigest() # def tell_info(self): # data = self.get_obj_by_id() # for item in data: # print('%-10s: %-20s' % (item,data[item])) # # print(data) # 将老师信息序列化后保存到<a href="/tag/wenjian/" target="_blank" class="keywords">文件</a> def save(self): user_data = {'name': self.name,'level': self.level,'salary': self.salary,'id_hash': self.id_hash} with open('db_file/%s'%self.id_hash,f) # # 通过id反序列化老师信息 # def get_obj_by_id(self): # with open('db_file/%s'%self.id_hash,'rb') as f: # return pickle.load(f)</code></pre>
在Python3中,super()括号里面可以为空,不过也可以写成super(本类的名字,self)
像上面的**super().__init__(name,gender),**
可以写成**super(Teacher,self).__init__(name,gender)**
- 什么是组合?
一个对象的属性是来自另外一个类的对象,称之为组合
- 为何用组合?
# coding: utf-8 # @Time : 2018/10/24 10:27 AM # @Author : MUSIBII # @Email : shaozuanzuan@gmail.com # @File : zuhe.pyclass OldboyPeople:
school = 'Oldboy'def __init__(self,gender): self.name = name self.age = age self.gender = gender
class OldboyStudent(OldboyPeople):
def init(self,gender):
super().init(name,gender)def choose_course(self): print('%s is choosing course' % self.name)
class OldboyTeacher(OldboyPeople):
def init(self,salary):
OldboyPeople.init(self,gender)
self.level = level
self.salary = salarydef <a href="/tag/score/" target="_blank" class="keywords">score</a>(self,stu,num): stu.num = num print('老师%s给学生%s打分%s' % (self.name,stu.name,num))
class Course:
def init(self,course_name,course_price,course_period):
self.course_name = course_name
self.course_price = course_price
self.course_period = course_perioddef tell_course(self): print('课程名:<%s> 价钱:[%s] 周期:[%s]' % (self.course_name,self.course_price,self.course_period))
course = Course('python',3000,'5months')
stu1 = OldboyStudent('musibii',18,'male')
stu1.course = courseprint(stu1)