Python面向对象篇(2)-继承
在发表本篇随笔的时候,距离上一次发已经有一个多月了,很多朋友私信我为什么不持续更新了,在这里先跟大家说声抱歉。因为年底的工作较为繁重,实在分不出精力,更重要的也是在思考后面进阶的部分要按怎样的顺序写,对于初学者来说更友好,更容易理解,希望我的文章能帮到更多的喜欢python,想要学习python的人,前面的文章我也会及时更新知识点和排版,2018年希望喜欢我文章的人能继续支持,谢谢大家!
1、 继承
1.1 继承的实现
对一个程序员来说,如果他的代码中出现大量重复的代码,那么很快就会面临被开除了。函数可以有效的避免代码的重复,而继承,也能在最大程度解决这一问题,如果两个类有很多相同的属性,就可以通过继承来实现。
继承指的是类与类之间的关系,在Python中,继承分为单继承和多继承,即可以同时继承一个或多个类,被继承的类称为基类或超类,继承其他类的类称为子类或派生类。继承的方式很简单,通过“class+子类(父类)”的方式来实现,如下:
class parent_class_1:
pass #创建父类1
class parent_class_2:
pass #创建父类2
class sub1_class(parent_class_1):
pass #创建子类sub_class,继承父类parent_class_1(单继承)
class sub2_class(parent_class_1,parent_class_2):
pass #创建子类sub_class,通过“,”来分割,继承多个父类
在使用多继承的时候,如果继承的所有父类中,都有相同的方法名,即父类1中有write()方法,父类2中也有一个write()方法,当一个类同时继承这两个类,如果子类调用调用write()方法,该调用哪个父类的write()方法?此时会遵循一个原则,先继承的类中的方法会重写后继承的类中的方法(使其不可访问),所以注意继承父类的顺序是很重要的。多继承的方法也是不建议多使用的。
通过__base__方法和__bases__方法来查看继承的父类是哪一个。
__base__:从左到右查看子类第一个继承的父类,只返回第一个父类;
__bases__:查看子类继承的所有类,返回继承的所有父类。
print(sub1_class.__base__) #查看sub1_class继承的第一个父类 print(sub2_class.__bases__) #查看sub2_class 都继承了哪些父类
运行结果:
<class '__main__.parent_class_1'> (<class '__main__.parent_class_1'>, <class'__main__.parent_class_2'>)
继承,就像是继承家产一样,父亲有的东西,继承过来自己就也有了,所以,当一个类继承了某一个父类的话,那么这个子类就可以调用继承的父类中的所有属性。可以看下如下这个简单的例子:
class parent_class: #创建父类
money = 1000000000 #定义一个类属性:money
def cost(self): #定义一个方法:cost()
print("如果你继承了,我的钱你都可以花")
class sub_class(parent_class): #创建子类,继承parent_class这个父类
pass #类代码逻辑为空
print(sub_class.money) #通过子类,调用父类的属性
sub_class.cost(sub_class) #通过子类,调用父类的方法
运行结果:
1000000000
11 如果你继承了,我的钱你都可以花
在上面这个例子中,在创建父类parent_class的时候,定义了一个类属性和类方法,而在创建子类sub_class的时候,类里面没有写任何逻辑代码,仅仅是在创建子类的时候继承了父类,那么也就意味着,这个子类就可以调用父类中的所有属性和方法。
1..2 继承的基本原理和继承顺序
在Python中,对于定义的每一个类,Python内部都会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表。
Python如果继承了多个类,那么在继承的顺序上,遵循两种方式:深度优先和广度优先,如下图:
当类是经典类的时候,多继承的情况下,会按照深度优先的方式查找
当类是新式类的时候,多继承的情况下,会按照广度优先的方式查找
在Python3中,创建的类都是新式类,所以都会按照广度有限的方式进行查找什么是广式类呢?可以理解为:横向优先检索。如下:
class A:
def test(self):
print("A")
class B(A):
def test(self):
print("B")
class C(A):
def test(self):
print("C")
class D(C,B):
pass
#__mro__方法,打印出类继承的线性顺序列表
print(D.__mro__)
D.__mro__运行结果:
(<class '__main__.D'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
在继承顺序上,Python内部会在mro列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止,mro线性列表包含以下两个原则:
1)子类优于父类,即如果在D类定义了test函数,那么它会优先调用自己类内部的test方法
2)继承多个父类会根据它们在列表中的顺序被子类继承,即如果是class D(B,C)那么继承顺序就会变为D->B->C->A
1.3 派生
子类也叫做派生类,在继承了父类的所有属性和方法后,也允许添加、修改或重新定义自己内部的属性和方法,如果新定义的属性或方法与继承来的父类中的属性和方法重名时,在调用时会优先调用自己内部的属性或方法,对父类中的代码不会有任何的影响。
class Dog:
def jump(self):
print("我是父类:我非常爱跳")
class Tidy(Dog):
pass
s_1 = Tidy()
s_1.jump()
运行结果:
我是父类:我非常爱跳
在这段代码中,创建了一个父类Dog,并定义了一个方法jump(),接下来创建了一个子类(派生类)来继承Dog类,子类内部没有写任何逻辑代码,通过实例s_1调用父类中的方法jump()。运行结果如上所示。
如果在子类中也定义了一个方法名是jump()的方法呢?调用时该运行哪一个?
class Dog:
def jump(self):
print("我是父类:非常爱跳")
class Tidy(Dog):
def jump(self):
print("我是子类:非常爱跳")
s_1 = Tidy()
s_1.jump()
运行结果:
我是子类:非常爱跳
实例s_1是类Tidy()的实例化对象,当调用类的属性或方法时,会优先在所属类的内部查找,如果找不到,才会去继承的父类中查找,运行结果如上所示。
内建的isinstance()函数和issubclass()函数的使用
isinstance(x, A_tuple):判断一个对象是否是某一个数据类型,返回一个布尔值。传入两个值,第一个值是需要判断的对象,第二个值是数据类型。如判断实例s_1是不是属于类Dog:isinstance(s_1,Dog)
issubclass(x, A_tuple):判断一个类是不是另一个类的子类,返回布尔值,传入两个值,第一个值子类的类名,第二个值是父类的类名。如判断Tidy是不是Dog的子类:issubclass(Tidy,Dog)
下面再来一个稍微复杂一点的。
class Foo:
def f1(self):
print("Foo.f1")
def f2(self):
print("Foo.f2")
self.f1()
class Bar(Foo):
def f1(self):
print("Bar.f1")
s_1 = Bar()
s_1.f2()
这段代码,运行结果是什么?为什么是这个结果?
1.4 组合
通过组合的方式,也可以避免代码的大量重复,组合指的就是,在一个类中,以另一个类作为它的数据属性。当类被定义后,
class Dog: #创建一个小狗的类
def jump(self): #定义一个jump()方法
print("小狗正在跳")
class Tidy:
def __init__(self,name): #构造函数
self.name = name
self.jump = Dog() #通过组合的方式,将Dog类赋值给self.jump属性
s_1 = Tidy("AA")
s_1.jump.jump() #调用Dog类中的jump()方法
这就是两个类之间的组合,可以通过将类赋值给其他类的一个数据属性,在调用的时候,s1_jump,实质上就是在调用Dog这个类,再通过.jump()方法,实质上也就是—》Dog.jump,这样就不难理解s_1.jump.jump()这条代码了。
组合和继承都能很好、有效的利用已定义好的资源,不用重复去写相同的代码,但是二者的使用场景也有一下不同。继承可以理解成包含的意思,当类之间有很多共同的属性时,就比较适合将这些共有的属性做成一个基类来继承。比如,人都有一个鼻子,两只眼睛..不同的是高矮胖瘦…..;而组合,可以理解为。
class School:
def __init__(self,name,addr):
self.name = name
self.addr = addr
class Course:
def __init__(self,name,period,school):
self.name = name
self.period = period
self.school = school s_1 = School("一中","小石路")
s_2 = School("二中","中石路")
s_3 = School("三中","大石路") msg="""
1:一中
2:二中
3:三中
""" while True:
print(msg)
menu = {
"":s_1,
"":s_2,
"":s_3
} choice = input("请输入你的选择:")
school_obj = menu[choice]
name = input("请输入课程名")
period = input("请输入学期")
new_course = Course(name,period,school_obj)
print("%s 属于%s"%(new_course.name,new_course.school.name))
1.5 接口继承
在子类中继承父类的方法
继承的最大作用在于可以减少代码的重复利用,子类继承过来父类定义的数据属性和方法后如何调用呢:
class Vehicle:
def __init__(self,name,type,speed,):
self.name = name
self.type = type
self.speed = speed class Bus(Vehicle):
def __init__(self,name,type,speed,line):
Vehicle.__init__(self,name,type,speed)
self.line = line
def run(self):
print("%s路公交车马上就到了"%self.line)
s_1 = Bus("公家车","天然气","10km/h",1)
s_1.run()
运行结果:
1路公交车马上就到了!
这样的方法有如下两个缺点:第一:如果父类的名称发生了变化,子类的代码也要相应进行改变。第二:Python是允许多继承的语言,如上所示的方法在多继承中就要重复很多次调用父类的__init__()方法。为了解决这些问题,就引入了super()机制。
class Vehicle:
def __init__(self,name,type,speed,):
self.name = name
self.type = type
self.speed = speed
def run(self):
print("我是父类:开动了") class Bus(Vehicle):
def __init__(self,name,type,speed,line):
# Vehicle.__init__(self,name,type,speed)
super(Bus,self).__init__(name,type,speed)
self.line = line
def run(self):
# Vehicle.run(self)
super().run()
print("%s路公交车马上就到了"%self.line)
s_1 = Bus("公家车","天然气","10km/h",1) s_1.run()
从运行结果上来看,普通的这种继承方式和super继承是一样的,但其实两种方式的内部运行机制是不一样的。在super机制里可以保证公共父类仅被执行一次,至于执行的顺序,是按照mro进行的(类名.__mro__)。
两者在运行机制上有什么区别呢?
class A:
def __init__(self):
print("test A")
class B(A):
def __init__(self):
print("test B")
A.__init__(self)
class C(A):
def __init__(self):
print("test C")
A.__init__(self)
class D(C,B):
def __init__(self):
print("test D")
B.__init__(self)
C.__init__(self)
D()
运行结果:
test D
test B
test A
test C
test A
super
class A:
def __init__(self):
print("test A")
class B(A):
def __init__(self):
print("test B")
super(B,self).__init__()
class C(A):
def __init__(self):
print("test C")
super(C,self).__init__()
class D(C,B):
def __init__(self):
print("test D")
super(D,self).__init__()
D()
运行结果:
test D
test C
test B
test A
super(B,self).__init__()实质上就是调用了super类的初始化函数,产生一个super对象,仅仅是简单的记录了类的类型和具体的实例,super(类名, self).func的调用并不是用于调用当前类的父类的func函数;Python的多继承类是通过mro的方式来保证各个父类的函数被逐一调用,而且保证每个父类函数
只调用一次(如果每个类都使用super);混用super类和非绑定的函数是一个危险行为,这可能导致应该调用的父类函数没有调用或者一个父类函数被调用多次。
Python面向对象篇(2)-继承的更多相关文章
- Python面向对象三要素-继承(Inheritance)
Python面向对象三要素-继承(Inheritance) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.继承概述 1>.基本概念 前面我们学习了Python的面向对象三 ...
- Python面向对象中的继承、多态和封装
Python面向对象中的继承.多态和封装 一.面向对象的三大特性 封装:把很多数据封装到⼀个对象中,把固定功能的代码封装到⼀个代码块, 函数,对象, 打包成模块. 这都属于封装思想. 继承:⼦类可以⾃ ...
- Python - 面向对象编程 - 多继承
继承的详解 https://www.cnblogs.com/poloyy/p/15216652.html 这篇文章讲的都是单继承,Python 中还有多继承 Python 多继承的背景 大部分面向对象 ...
- Python面向对象篇之元类,附Django Model核心原理
关于元类,我写过一篇,如果你只是了解元类,看下面这一篇就足够了. Python面向对象之类的方法和属性 本篇是深度解剖,如果你觉得元类用不到,呵呵,那是因为你不了解Django. 在Python中有一 ...
- Python面向对象之类的继承(2)
1.除了封装,Python面向对象还有继承这一功能,如下代码,这是简单的继承功能. class Animal: def chi(self): print(self.name+' 吃') def he( ...
- Python()- 面向对象三大特性----继承
继承: 继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类(基类或超类),新建的类是所继承的类的(派生类或子类) 人类和狗 有相同的属性, 提取了一个__init__方法,在这 ...
- python 面向对象八 多继承
python是支持多继承的,在设计类的继承关系时,通常,主线都是单一继承下来的.但是,如果需要“混入”额外的功能,通过多重继承就可以实现,这种设计通常称之为MixIn. 为了更好地看出继承关系,以Mi ...
- python——面向对象篇之异常和反射
内置函数isinstance和issubclass 1.1 isinstance用法: isinstance(string,str) 判断第一个参数是否是第二个参数的子集,例如: print isin ...
- Python面向对象篇(1)-类和对象
面向对象编程 1.编程范式 我们写代码的目的是什么?就是为了能够让计算机识别我们所写的代码并完成我们的需求,规范点说,就是通过编程,用特定的语法+数据结构+特殊算法来让计算机执行特定的功能,实现一 ...
随机推荐
- PHP面试题:HTTP中POST、GET、PUT、DELETE方式的区别
HTTP定义了与服务器交互的不同的方法,最基本的是POST.GET.PUT.DELETE,与其比不可少的URL的全称是资源描述符,我们可以这样理解:url描述了一个网络上资源,而post.get.pu ...
- win7系统如何在防火墙里开放端口
用到的端口需要在防火墙里开放,win7的比XP的要复杂一些,此方法同样适用于server2008系统 方法/步骤 1 依次点击"开始"-"控制面板"-" ...
- 基于Vue的页面切换左右滑动效果
HTML文本页面: <template> <div id="app> <transition :name="direction" mode= ...
- php中session_start()函数的作用
php中session_start()函数的作用 用$_SESION之前必须要session_start()----其中之一的功能,$_SESSION是服务器端的cookie,相当一个大数组(浏览器关 ...
- redux学习日志:关于异步action
当我们在执行某个动作的时候,会直接dispatch(action),此时state会立即更新,但是如果这个动作是个异步的呢,我们要等结果出来了才能知道要更新什么样的state(比如ajax请求),那就 ...
- HTTP常用状代码
2XX 成功 204 Not Content 请求处理成功,但没有资源可以返回. 1 put请求:该资源已存在于服务器上 2 delete请求:该资源已从服务器上删除 200 OK 请求正常处理 20 ...
- rpm包
rpm包有什么命名规则与依赖? 命令规则: 包名-版本号.发布次数-linux平台.l.硬件平台.rpm 依赖: 树型依赖:a --> b --> c 安装a包需要安装b包,安装b包需要安 ...
- Linux Shell 编程语法
原文地址:http://www.cnblogs.com/fhefh/archive/2011/04/13/2014967.html.感谢作者的无私分享 编写代码 在计划好要程序干什么以及如何使用程序的 ...
- Struts2 (二)
1 自定义结果视图 1.1 自定义一个类实现com.opensymphony.xwork2.Result接口. package com.xuweiwei.action; import com.open ...
- 《CSS动画实用技巧》课程笔记
概述 这是我学习[CSS动画实用技巧][1]的课程笔记 常用动画属性--transition [常用动画属性--transition][2] .change img{ display:block; w ...