⾯向对象编程设计与开发
⼀、什么是⾯向对象的程序设计
1、何为数据结构?
数据结构是指相互之间存在⼀种或多种特定关系的数据元素的集合,如列表、字典。
2、何为编程?
编程是指程序员⽤特定的语法+数据结构+算法,组成的代码,告诉计算机如何执⾏任务的过程。
3、何为编程范式?
实现⼀个任务的⽅式有很多种,对这些不同的编程⽅式的特点进⾏归纳总结得出的编程⽅式类别即为编程范式。
⼤多数语⾔只⽀持⼀种编程⽅式,也有些⽀持多种的。
两种最重要的编程范式:⾯向过程编程和⾯向对象编程。
4、⾯向过程编程(Procedural programming)
⾯向过程编程的核⼼是“过程”,过程即指解决问题的步骤。
优点:将复杂的问题流程化,简单化。
缺点:可扩展性差,后期如果要更改某⼀程序功能是,可能存在“牵⼀发⽽动全⾝”,软件的维护难度会越来越⾼。
应⽤场景:适⽤于那些功能⼀旦实现就不会轻易更改的情况,如Linux内核,git,Apache HTTP Server等。
5、⾯向对象编程(Object Oriented Programming)
⾯向对象,核⼼就是“对象”⼆字,对象就是“特征”与“技能”的结合体。
优点:可扩展性强,易更改,易维护。
缺点:编程复杂度⾼
应⽤场景:应⽤于⽤户需求经常变化的场景,如互联⽹应⽤、企业内部应⽤、游戏等。
⾯向对象编程:就是利⽤“类”和“对象”来创建各种模型来实现对真实世界的描述,主要⽤于解决软件开
发中可扩展性的问题。
6、⾯向对象三⼤特性:继承、多态、封装
⼆、类与对象
1、python中⼀切皆对象。
2、对象:指特征与技能的结合体。
3、类:指⼀系列对象相似的特征与技能的结合体,类就相当于⼀个模型。
4、站在不同的⾓度,得到的分类是不⼀样的。
5、类与对象的产⽣
在现实世界中:先有对象,后有类
在程序中:先定义类,后调⽤类来产⽣对象
与函数的使⽤是类似的:先定义函数,后调⽤函数,类也是⼀样的:在程序中需要先定义类,后调⽤
类。不⼀样的是:调⽤函数会执⾏函数体代码,返回的是函数体执⾏的结果,⽽调⽤类会产⽣对象,返回的是对象。
6、如何定义类
在程序中,⽤class关键字定义⼀个类,类的特征⽤变量标识,即数据属性,类的技能⽤函数标识,即函数属性。
类在定义阶段就会被执⾏,会产⽣新的名称空间,⽤来存放类的变量名和函数名,可以通过“类名.__dict__”查看类的名称空间,返回的是⼀个字典。对于经典类来说,我们可以通过该字典操作类名称空间的名字,但新式类有限制。
7、如何使⽤类
类的两⼤⽤途:对属性的操作、实例化对象。
8、使⽤__init__⽅法定制对象独有的特征
__init__⽅法被称为构造⽅法或初始化⽅法。在对象实例化的时候就会⾃动调⽤该⽅法,实现对象的初始化操作,在__init__⽅法中不能有return返回值
9、对象属性的操作
10、类的属性查与绑定⽅法
类有两种属性:数据属性和函数属性。
1、类的数据属性是所有对象共享的
2、类的函数属性在没有被任何装饰器修饰的情况下是绑定给对象⽤的,称为绑定到对象的⽅法,绑定到不同的对象就是不同的绑定⽅法,对象绑定⽅法时,会把对象本⾝当作第⼀个参数传⼊,即self==对象名。绑定到对象的⽅法的这种⾃动传值的特征,决定了在类中定义的函数都要默认写⼀个参数self,self可以是任意名字,但是约定俗成的写为self。
三、继承
继承指类与类之间的⼀种什么“是”什么的关系。⽤于解决代码重⽤的问题。继承是⼀种新建类的⽅式,新建的类可以继承⼀个或多个⽗类,⽗类可以称为基类或超类,新建的类称为⼦类或派⽣类。
1、python中类的继承分为:单继承和多继承
2、查看继承:
3、继承中的属性查:
调⽤f2⽅法,发现对象b中没有该⽅法,再去b的类Bar中,也没有,接着去Bar类的⽗类Foo中,到了,就打印“from Foo.f2”,在Foo中还有语句“self.f1()”,这时,程序会⼜在对象b中名为f1的⽅法,发现没有,再去b的类Bar中,到了,就打印“form Bar.f1”
4、python中经典类与新式类
在python3中如果不指定继承哪个类,默认就会继承Object类,⽽继承了Object类的类就叫做新式类。
python2中如果不指定继承哪个类也不会默认去继承Object类,⽽没有继承Object类的类就叫做经典类。⽽显⽰的指定继承Object类的类才是新式类。
经典类和新式类的不同就在于属性查的顺序不同,经典类是深度优先(先⼀条路⾛到底),即先⾃⼰类内,如果没有就右边第⼀个⽗类,没到继续从这个⽗类的⽗类中依次类推直到到最上⼀级的⽗类也没到再右边第⼆个⽗类,然后再重复之前的过程,直到所有⽗类⼀遍没到就报错;⽽新式类是⼴度优先,先按⽗类顺序逐个查,如果没到,最后才会去object类,如果没有到指定属性就报错。可以通过“类名.mro()”或“类名.__mro__”查看新式类继承中的属性查顺序。
# python2中分有新式类和经典类,py3中只有新式类
#py2中,经典类:没有继承object的类以及它的⼦类
class Foo:
pass
class Bar(Foo):
pass
# 在py2中,新式类:继承object的类,以及它的⼦类都称之为新式类
class Foo(object):
pass
class Bar(Foo):
pass
# 在py3中,新式类:默认都继承object
class Foo: # 等同于class Foo(object):
pass
class Bar(Foo):
pass
下⾯是我在别处到的⽐较经典的能够很好的展现深度优先和⼴度优先查的图:
5、在⾃类中调⽤⽗类的⽅法
1)指名道姓,即⽗类名.⽗类⽅法(),这种⽅式不依赖于继承
2)使⽤super(),这种⽅式依赖于继承的,并且即使没有直接继承关系,super仍然会按照mro继续往后查
6、派⽣
⼦类可以给⾃⼰添加新的属性,或在⾃⼰这⾥重新定义⽗类的属性,⽽不会影响⽗类,⼀旦重新定义了⾃⼰的属性且与⽗类同名,那么调⽤该属性时,就会以⾃⼰重新定义的为准。
四、组合
软件重⽤的重要⽅式除了继承之外还有另外⼀种⽅式,即:组合
组合指的是,在⼀个类中以另外⼀个类的对象作为数据属性,称为类的组合
组合是⼀种什么“有”什么的关系,如教师有课程,学⽣有课程。
五、接⼝类与抽象类
接⼝类是⽤于规范⼦类的⽅法名定义⽤的,接⼝提取了⼀类共同的函数,可以把接⼝当做⼀个函数的集合。继承接⼝类的⼦类可以不存在任何逻辑上的关系但是都需要实现某些共同的⽅法,为了让这些⼦类的⽅法名能够统⼀以便之后调⽤这些⽅法时不需要关注具体的对象就⽤接⼝类规范了这些⽅法的名字,⼦类⼀旦继承了接⼝类就必须实现接⼝类中定义的⽅法,否则在⼦类实例化的时候就会报错,⽽接⼝类本⾝则不需要实现去实现这些⽅法。
上⾯的代码只是看起来像接⼝,其实并没有起到接⼝的作⽤,⼦类完全可以不⽤去实现接⼝,⽐如:在People类中,完全可以把run改为walk等⽅法名,也就是说,此时的⽗类对⼦类的调⽤⽅式,不具有约束⼒,
这就⽤到了抽象类:
抽象类的作⽤和接⼝类⼀样,只是继承它的⼦类⼀般存在⼀些逻辑上的关系。
抽象类是⼀个特殊的类,它的特殊之处在于只能被继承,不能被实例化,
如果说类是从⼀堆对象中抽取相同的内容⽽来的,那么抽象类就是从⼀堆类中抽取相同的内容⽽来的,内容包括数据属性和函数属性。
从设计⾓度去看,如果类是从现实对象抽象⽽来的,那么抽象类就是基于类抽象⽽来的。
抽象类与普通类的区别:抽象类中只能有抽象⽅法(没有实现功能),该类不能被实例化,只能被继承,且⼦类必须实现抽象⽅法。
接⼝类与抽象类的区别:接⼝只强调函数属性的相似性。抽象类既包括函数属性⼜包括数据属性。
所以说,抽象类同时具备普通类和接⼝类的部分特性。
六、多态
1、多态指⼀类事物的多种形态,如动物类有:⼈、猫、狗。
2、多态性指在不考虑对象类型的情况下去使⽤对象,不同的对象可以调⽤相同的⽅法得到不同的结果。有点类似接⼝类的感觉。
3、多态性的好处:
1)增加程序灵活性
2)增加程序可扩展性
4、静态多态性:⽐如不管你是列表还是字符串还是数字都可以使⽤+和*。
5、动态多态性:调⽤⽅法
七、封装
封装就是把类中的属性和⽅法定义为私有的。
就是在属性名或⽅法名前加双下划线,⽽⼀旦这样定义了属性或⽅法名后,python会⾃动将其转换为“_类名__属性名(⽅法名)”的格式。相当于⼀种变形操作。
在类的内部调⽤还是⽤双下划线加属性名或⽅法名,在类的外部调⽤就要⽤_类名__属性名(⽅法名)。
这种变形操作只发⽣在类定义初期,即在类定义完成后是不起作⽤的。
⽗类的私有属性和⽅法,⼦类⽆法对其进⾏修改。
观察以上两个程序的运⾏结果,就很显然的发现加上“__”前后运⾏结果发⽣了变化。因为在类定义初期,类A⾥⾯的属性已经发⽣了变形。
封装的意义:1)封装数据:将数据属性封装起来,对外提供操作该数据的接⼝,在接⼝上附上⼀些操作数据的限制,以此来完成严格的操作数据属性的控制;
2)封装函数:将复杂的实现过程封装起来,隔离复杂度。
⼋、类的装饰器
1、property属性装饰器:property是⼀种特殊的属性,访问它时会执⾏⼀段功能(函数),然后返回⼀个值。
加上property属性装饰器后,将类内的⽅法的调⽤⽅式设置成和数据属性⼀样。
将类中的函数⽅法定义为property特性以后,对象在调⽤该⽅法时就可以和调⽤数据属性⼀样,使⽤“对象名.属性名”的格式,⽽不⽤加括号。
但此时的“对象名.属性名”是不能被赋值的,因为它只是长得像数据属性,其实质还是函数。
这个装饰器还有和其配套的setter、deleter:
2、 staticmethod静态⽅法装饰器:将类内的⽅法变成普通的函数,即⾮绑定⽅法,类和对象都可以调⽤,不存在⾃动传值。
3、classmethod类⽅法装饰器,将⽅法绑定给类,类调⽤该⽅法时,将类名作为第⼀参数,⾃动传⼊。
九、绑定⽅法与⾮绑定⽅法
在类内部定义的函数,分为两⼤类:
(⼀)绑定⽅法:绑定给谁就应该由谁来调⽤,谁来调⽤就会⾃动把调⽤者当做第⼀个参数传递给函数
1、绑定到对象的⽅法:在类内定义的没有被任何装饰器修饰的函数
2、绑定到类的⽅法:在类内定义的被装饰器classmethod修饰的函数
(⼆)⾮绑定⽅法:没有⾃动传值这⼀说,就是类中定义的⼀个普通函数,类和对象都可以使⽤
在类内部⽤staticmethod装饰的函数,不与类或者对象绑定
⼗、isinstance和type的区别以及issubclass
isinstance和type都可以⽤于判断对象和指定类间的关系,但是isinstance的判断没有type准确,它⽆法正确判断⼦类的对象和其⽗类的关系
issubclass⽤于判断给定的两个类,前者是否是后者的⼦类
⼗⼀、反射
反射是指通过字符串映射到对象(或类)的属性
hasattr(对象或类名,‘属性或⽅法名’) 判断指定的对象或类中是否存在指定的属性或⽅法,有返回True
getattr(对象或类名,'属性或⽅法名',default=None) 获取对象或类的指定属性值或⽅法的内存地址,不存在就返回None
setattr(对象或类名,‘新属性名’,新属性值) 给对象或类添加新的属性或⽅法
delattr(对象或类名,‘新属性名’) 删除之前添加的属性
反射当前模块成员:
输⼊命令,进⾏⽂件的下载与上传:
反射的好处:
1)实现可插拔机制。你可以事先把主要的逻辑写好(只定义接⼝),然后后期再去实现接⼝的功能
2)动态导⼊模块
⼗⼆、类的内置⽅法
1、__setattr__,__delattr__,__getattr__
2、__getattribute__
3、描述符(__get__,__set__,__delete__)
描述符本质就是⼀个新式类,在这个新式类中,⾄少实现了__get__(),__set__(),__delete__()中的⼀个,这也被称为描述符协议。
__get__():调⽤⼀个属性时触发
__set__():为⼀个属性赋值时触发
__delete__():采⽤del删除属性时触发
描述符的作⽤是⽤来代理另外⼀个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)
linux内核设计与实现 pdf严格来说,只有将描述符定义成⼀个类的类属性时,对这个类属性进⾏增删改查时便会触发执⾏__get__,__set__,__delete__
描述符分两种:
1)数据描述符:⾄少实现了__get__()和__set__()
class Foo:
def __set__(self, instance, value):
print('set')
def __get__(self, instance, owner):
print('get')
2)⾮数据描述符:没有实现__set__()
class Foo:
def __get__(self, instance, owner):
print('get')
注意事项:
描述符本⾝应该定义成新式类,被代理的类也应该是新式类;
必须把描述符定义成这个类的类属性,不能为定义到构造函数中;
要严格遵循该优先级,优先级由⾼到底分别是 1.类属性 2.数据描述符 3.实例属性 4.⾮数据描述符 5.不到的属性触发__getattr__()
4、__setitem__,__getitem__,__delitem__
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论