继承介绍
继承指的是类与类之间的关系,是一种什么是什么的关系,功能之一就是用来解决代码重用问题。
继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类
python 中分为单继承和多继承
python中类可以继承多个类.
class ParentClass1: #定义父类
pass
class ParentClass2: #定义父类
pass
class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass
pass
class SubClass2(ParentClass1,ParentClass2): # python支持多继承,用逗号分隔开多个继承的类
pass
查看继承的类:
res=SubClass2.__bases__
print(res)
#__base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类
输出:
(<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)
经典类和新式类
1.只有在python2中才分新式类和经典类,python3中统一都是新式类
2.在python2中,没有显式的继承object类的类,以及该类的子类,都是经典类
3.在python2中,显式地声明继承object的类,以及该类的子类,都是新式类
3.在python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类
提示:如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如str)的实现。
res=ParentClass1.__bases__
print(res)
# 输出:
(<class 'object'>,)
在开发程序的过程中,如果我们定义了一个类A,然后又想新建立另外一个类B,但是类B的大部分内容与类A的相同时
我们不可能从头开始写一个类B,这就用到了类的继承的概念。
通过继承的方式新建类B,让B继承A,B会‘遗传’A的所有属性(数据属性和函数属性),实现代码重用
在不依赖继承属性的情况下,子类在调用父类的属性时,可以直接通过函数调用的方式调用父类的属性:
class ParentClass: #定义父类
def __init__(self,name,age):
self.name=name
self.age=age
class SubClass(ParentClass):
def __init__(self,name,age,hobby,title):
ParentClass.__init__(self,name,age) # 直接以函数的方式调用父类的属性
self.hobby=hobby
self.title=title
obj=SubClass('tom',22,'reading','Student')
print(obj.__dict__)
注意:类的属性引用,会先从实例中找,然后去类中找,然后再去父类中找...直到最顶级的父类。
组合
组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合
组合与继承都是有效地利用已有类的资源的重要方式。但是二者的概念和使用场景皆不同:
1.继承的方式
通过继承建立了派生类与基类之间的关系,它是一种'是'的关系,比如白马是马,人是动物。
当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好,比如老师是人,学生是人
2.组合的方式
用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如教授有生日,教授教python和linux课程,教授有学生s1、s2、s3...
示例:
# coding=utf-8
class People:
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex=sex
class Course:
def __init__(self,name,period,price):
self.name=name
self.period=period
self.price=price
def tell_info(self):
print('%s %s %s' %(self.name,self.period,self.price))
class Teacher(People):
def __init__(self,name,age,sex,job_title):
People.__init__(self,name,age,sex)
self.job_title=job_title
self.course=[]
self.students=[]
class Student(People):
def __init__(self,name,age,sex):
People.__init__(self,name,age,sex)
self.course=[]
egon=Teacher('egon',18,'male','advance')
s1=Student('bob',18,'female')
python=Course('python','3mons',3000.0)
linux=Course('linux','3mons',3000.0)
#为老师egon和学生s1添加课程
egon.course.append(python)
egon.course.append(linux)
s1.course.append(python)
#为老师egon添加学生s1
egon.students.append(s1)
#使用
for obj in egon.course:
obj.tell_info()
print(egon.__dict__)
输出结果:
python 3mons 3000.0
linux 3mons 3000.0
{'name': 'egon', 'age': 18, 'sex': 'male', 'job_title': 'advance', 'course': [<__main__.Course object at 0x000000410AF46CC0>, <__main__.Course object at 0x000000410AF46CF8>], 'students': [<__main__.Student object at 0x000000410AF46C88>]}
继承实现的原理
1、继承顺序
* python 类可以继承多个类,java和C#中则只能继承一个类
* Python 的类如果继承了多个类,那么其查找父类的顺序有两种:深度优先
和广度优先
* 当类是经典类时,多继承情况下,会按照深度优先的方式查找
* 当类是新式类时,多继承情况下,会按照广度优先的方式查找
推荐使用新式类,如果当前类或者父类继承了object类,那么该类便是一个新式类,否则便是经典类。(经典类之存在于python2中)
2、继承原理(python如何实现的继承)
python实现继承的原理是: 对于定义的每一个类,python都会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序表。
class A:
pass
class B(A):
pass
class C:
pass
class D(C,B):
pass
obj=D()
print(D.mro())
# 输出列表:
[<class '__main__.D'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
- 子类会先于父类被检查
- 多个父类会根据它们在列表中的顺序被检查
- 如果对下一个类存在两个合法的选择,选择第一个父类
子类中调用父类的方法
1、通过指定父类方法名的方式去调用。(不利用继承的属性)
2、通过使用super()
class Subway(Vehicle): #地铁
def __init__(self,name,speed,load,power,line):
#super(Subway,self) 就相当于实例本身, 在python3中super()等同于super(Subway,self)
super().__init__(name,speed,load,power) # 调用父类的init方法.
self.line=line
注意:
当你使用super()函数时,Python会在MRO列表上继续搜索下一个类。只要每个重定义的方法统一使用super()并只调用它一次,那么控制流最终会遍历完整个MRO列表,每个方法也只会被调用一次(注意注意注意:使用super调用的所有属性,都是从MRO列表当前的位置往后找,千万不要通过看代码去找继承关系,一定要看MRO列表)
绑定方法与非绑定方法
类中的函数可以定义为两大类,一种是绑定方法,另一种是非绑定方法。
1、绑定方法:绑定给谁,谁来调用就自动将它本身当作第一个参数传入:
- 绑定到类的方法:用classmethod装饰器装饰的方法。
为类量身定制,自动将类当作第一个参数传入。(其实对象也可调用,但仍将类当作第一个参数传入)
- 绑定到对象的方法:没有被任何装饰器装饰的方法。、
为对象量身定制对象,自动将对象当作第一个参数传入(属于类的函数,类可以调用,但是必须按照函数的规则来,没有自动传值那么一说)
2、 非绑定方法:用staticmethod装饰器装饰的方法
- 不与类或对象绑定,类和对象都可以调用,但是没有自动传值那么一说。就是一个普通工具而已。在非绑定方法中不需要(不会自动传值)传入对象和类,只是执行函数功能。
> 注意:与绑定到对象方法区分开,在类中直接定义的函数,没有被任何装饰器装饰的,都是绑定到对象的方法,可不是普通函数,对象调用该方法会自动传值,而staticmethod装饰的方法,不管谁来调用,都没有自动传值一说。