python--7、面向对象
什么是面向对象
对象,即抽象的一类事物中的某个具体的个体。这个世界中存在的一切皆为对象,不存在的也能创建出来。
较之面向过程的区别:
- 编程的复杂度远高于面向过程,不了解面向对象而立即上手基于它设计程序,极容易出现过度设计的问题。扩展性要求低的程序更适合用面向过程。
- 无法向面向过程的程序设计流水线式的可以很精准的预测问题的处理流程与结果,面向对象的程序一旦开始就由对象之间的交互解决问题,无法准确地预测最终结果。
应用场景:
需求经常变化的软件,一般需求的变化都是集中在用户层的。
类与对象
类,即类别。对象是特征与能力的结合体,而类则是一系列对象相似的特征与技能的结合体。
在程序中,一定是先有类,再有对象。
定义类的依据:
类中的属性
- 数据属性:只要是这个类产生的对象均会共享
- 函数属性:类中定义的函数,绑定给对象用的。被称为方法。绑定到对象的方法的特殊之处为,绑定给谁就由谁调用,把调用者当做第一个参数传进去,所以类中定义的函数都要默认写一个参数self。
对象之间的交互
class A:
state = 'china'
def __init__(self,name,HP,strength)
self.name = name
self.HP = HP
self.strength = strength
def war(self,enemy):
ememy.HP -= self.strength
print('%s剩余生命值%s'%(ememy.name,ememy.HP))
class B:
state = 'USA'
def __init__(self,name,HP,strength)
self.name = name
self.HP = HP
self.strength = strength
def war(self,enemy):
ememy.HP -= self.strength
print('%s剩余生命值%s'%(ememy.name,ememy.HP))
soldier = A('soldier',100,9)
criminal = B('criminal',100,5)
soldier.war(criminal)
继承
解决代码的重用。新建的类可以继承多个父类,这些父类称为基类或超类。新建的类称为派生类或子类。
只继承一个类称为单继承。继承多个类称为多继承。
查看继承
- 类.__base__ 只查看最左边继承的类
- 类.__bases__ 查看所有继承的类
经典类与新式类
1.只有在python2中才分新式类和经典类,python3中统一都是新式类
2.在python2中,没有显式的继承object类的类,以及该类的子类,都是经典类
3.在python2中,显式地声明继承object的类,以及该类的子类,都是新式类
4.在python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类
注:若未指定基类,默认会继承object类。object类是所有python类的基类,提供了一些通用方法。
继承与抽象
抽象
抽象即字面意思,抽取类似的或者比较像的部门。
抽象分为两个层次:先把相似的对象抽取成一个类,在把多个类抽取出一个父类。
继承
基于抽象出的结果,使用编程语言去实现它。必然要先抽象,再通过继承的方式去表达出抽象的结构
继承与重用性
#例如要分别创建一些猫和狗。可以分析出猫和狗都有的一些共性。如吃、喝、拉、撒。
class Animal:
def eat(self):
pass
def drink(self):
pass
def shit(self):
pass
def pee(self):
pass
class Cat(Animal):
def __init__(self, name):
self.name = name
self.breed = '猫'
def cry(self):
print('喵~喵')
class Dog(Animal):
def __init__(self, name):
self.name = name
self.breed='狗'
def cry(self):
print('汪~汪')
c = Cat('小白的小白猫')
c.drink()
d1 = Dog('胖子家的小瘦狗')
d1.eat()
#注:实例查找属性和方法时,先从实例中找,再去父类找,直到最顶级的父类。
#提示:用已经有的类建立一个新的类,这样就重用了已经有的软件中的一部分设置大部分,大大生了编程工作量,这就是常说的软件重用,不仅可以重用自己的类,也可以继承别人的,比如标准库,来定制新的数据类型,这样就是大大缩短了软件开发周期,对大型软件开发来说,意义重大.
派生
当然子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),需要注意的是,一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准了。
在子类中,新建的重名的函数属性,在编辑函数内功能的时候,有可能需要重用父类中重名的那个函数功能,应该是用调用普通函数的方式,即:类名.func(),此时就与调用普通函数无异了,因此即便是self参数也要为其传值。在py3中,推荐用super()调用父类的属性和方法。
组合与重用性
软件重用的重要方式除了继承之外还有另外一种方式,即:组合
组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合
把一个类的实力属性赋值为别的实例或者别的类。
组合与继承都是有效地利用已有类的资源的重要方式。但是二者的概念和使用场景皆不同,
1.继承的方式
通过继承建立了派生类与基类之间的关系,它是一种'是'的关系,比如白马是马,人是动物。
当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好,比如老师是人,学生是人
2.组合的方式
用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如教授有生日,教授教python和linux课程,教授有学生s1、s2、s3...
当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好
接口与归一化设计
Java中的接口
- Java的Interface接口的特征:
- 1)是一组功能的集合,而不是一个功能
- 2)接口的功能用于交互,所有的功能都是public,即别的对象可操作
- 3)接口只定义函数,但不涉及函数实现
- 4)这些功能是相关的,都是动物相关的功能,但光合作用就不适宜放到IAnimal里面了 */
为何要用接口
接口提取了一群类共同的函数,可以把接口当做一个函数的集合。
然后让子类去实现接口中的函数。
这么做的意义在于归一化,什么叫归一化,就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。
归一化的好处在于:
-
归一化让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。
-
归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合
2.1:就好象linux的泛文件概念一样,所有东西都可以当文件处理,不必关心它是内存、磁盘、网络还是屏幕(当然,对底层设计者,当然也可以区分出“字符设备”和“块设备”,然后做出针对性的设计:细致到什么程度,视需求而定)。
继承的两种用途
一:继承基类的方法,并且做出自己的改变或者扩展(代码重用):实践中,继承的这种用途意义并不很大,甚至常常是有害的。因为它使得子类与基类出现强耦合。
二:声明某个子类兼容于某基类,定义一个接口类(模仿java的Interface),接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能
继承实现的原理
1.继承顺序
2.继承原理(python如何实现的继承)
python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表,例如
F.mro() #等同于F.mro [<class 'main.F'>, <class 'main.D'>, <class 'main.B'>, <class 'main.E'>, <class 'main.C'>, <class 'main.A'>, <class 'object'>]
为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。 而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则: 1.子类会先于父类被检查 2.多个父类会根据它们在列表中的顺序被检查 3.如果对下一个类存在两个合法的选择,选择第一个父类
子类中调用父类的方法
class Vehicle: #定义交通工具类
Country='China'
def __init__(self,name,speed,load,power):
self.name=name
self.speed=speed
self.load=load
self.power=power
def run(self):
print('开动啦...')
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)
self.line=line
def run(self):
print('地铁%s号线欢迎您' %self.line)
super(Subway,self).run()
class Mobike(Vehicle):#摩拜单车
pass
line13=Subway('中国地铁','180m/s','1000人/箱','电',13)
line13.run()
super()与直接用父类名调用的区别
当你使用super()函数时,Python会在MRO列表上继续搜索下一个类。只要每个重定义的方法统一使用super()并只调用它一次,那么控制流最终会遍历完整个MRO列表,每个方法也只会被调用一次(注意注意注意:使用super调用的所有属性,都是从MRO列表当前的位置往后找,千万不要通过看代码去找继承关系,一定要看MRO列表)
天行健,君子以自强不息。
地势坤,君子以厚德载物。