面向对象编程设计与开发

一、什么是面向对象的程序设计

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删除属性时触发

描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)

严格来说,只有将描述符定义成一个类的类属性时,对这个类属性进行增删改查时便会触发执行__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__

 作用:将对象模仿成一个字典进行操作

5、__str__方法

打印对象时触发该方法,返回一个字符串类型的结果。

6、__del__方法

当对象在内存中被释放时,自动触发执行。

7、__doc__:输出类的描述信息,该属性无法被继承

8、 __module__ :表示当前操作的对象在那个模块

9、__class__ :  表示当前操作的对象的类是什么

10、__dict__ :查看类或对象中的所有成员,类调用时就打印类的所有属性,不包括实例属性。实例调用就打印所有实例的属性。

11、 __repr__  格式化输出  %r输出该方法的值,并且%s在没有__str__方法时也是输出该方法的值

12、__new__ 用于创建没有属性的对象,调用object的__new__即可不需要自己实现。可以利用该方法实现单例模式

13、  __call__ :对象加括号执行该方法

注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()
14、 __len__  :len()执行该方法
15、 __eq__ : ==运算输出该方法的值

16、__hash__ :hash执行该方法

 十三、元类

1、何为元类

元类就是类的类,是类的模板

元类是用来控制如何创建类的,正如类是创建对象的模板一样,而元类的主要目的是为了控制类的创建行为。

type是python的一个内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类实例化的对象。

2、exec函数

exec(参数一,参数二,参数三)

参数一:字符串形式的命令

参数二:全局作用域(字典形式),如果不指定,默认为globals()

参数三:局部作用域(字典形式),如果不指定,默认为locals()

3、python中一切皆对象,所以对象:

  1)可以被引用 ,x = obj

  2)可以当作函数的参数传入

  3)可以当作函数的返回值

  4)可以当作容器类的元素,l=[func,time, obj]

复制代码
class Foo:
    pass


obj = Foo()

# 一切皆对象,所以类Foo也是对象,即类也是对象
# 而一个对象就是一个类实例化而来,所以有一个类实例化产生了Foo
print(type(obj))  # <class '__main__.Foo'>
print(type(Foo))  # <class 'type'>  可以看出产生Foo的类是type
# python中用class关键字定义的类,产生这个类的类称之为元类,这个元类就是type
复制代码

4、定义类的两种方式

方式一:使用class关键字定义

方式二:自定义,即用type 这个元类去产生,模拟class创建类的过程

5、自定义元类控制类的行为

6、自定义元类控制类的实例化行为

看上面的程序,Foo里面有个__call__方法,当调用对象obj时被触发,

那么,Foo的类是元类,元类内部也应该有一个__call__方法,会在调用Foo时触发执行:

Foo(1,2,x=1)  # Foo.__call__(Foo,1,2,x=1)

所以我们可以模拟一个元类,在里面写上__call__方法,让它的子类在调用时触发__call__方法

7、自定义元类控制类的实例化行为的应用

有如下程序:

其实两个对象的内容是一样的,在实例化时根本不必造两个内存空间,这样定义就有点不合理了,这时就引入了单例模式

将这种方式用于元类中:

8、在元类中控制把自定义类的数据属性都变成大写

9、在元类中控制自定义的类无需init方法

十四、面向对象的软件开发流程

1.面向对象分析(object oriented analysis ,OOA)

软件工程中的系统分析阶段,要求分析员和用户结合在一起,对用户的需求做出精确的分析和明确的表述,从大的方面解析软件系统应该做什么,而不是怎么去做。面向对象的分析要按照面向对象的概念和方法,在对任务的分析中,从客观存在的事物和事物之间的关系,归纳出有关的对象(对象的‘特征’和‘技能’)以及对象之间的联系,并将具有相同属性和行为的对象用一个类class来标识。

建立一个能反映这是工作情况的需求模型,此时的模型是粗略的。

2 面向对象设计(object oriented design,OOD)

根据面向对象分析阶段形成的需求模型,对每一部分分别进行具体的设计。

首先是类的设计,类的设计可能包含多个层次(利用继承与派生机制)。然后以这些类为基础提出程序设计的思路和方法,包括对算法的设计。

在设计阶段并不牵涉任何一门具体的计算机语言,而是用一种更通用的描述工具(如伪代码或流程图)来描述

3 面向对象编程(object oriented programming,OOP)

根据面向对象设计的结果,选择一种计算机语言把它写成程序,可以是python

4 面向对象测试(object oriented test,OOT)

在写好程序后交给用户使用前,必须对程序进行严格的测试,测试的目的是发现程序中的错误并修正它。

面向对的测试是用面向对象的方法进行测试,以类作为测试的基本单元。

5 面向对象维护(object oriendted soft maintenance,OOSM)

正如对任何产品都需要进行售后服务和维护一样,软件在使用时也会出现一些问题,或者软件商想改进软件的性能,这就需要修改程序。

由于使用了面向对象的方法开发程序,使用程序的维护比较容易。

因为对象的封装性,修改一个对象对其他的对象影响很小,利用面向对象的方法维护程序,大大提高了软件维护的效率,可扩展性高。

在面向对象方法中,最早发展的肯定是面向对象编程(OOP),那时OOA和OOD都还没有发展起来,因此程序设计者为了写出面向对象的程序,还必须深入到分析和设计领域,尤其是设计领域,那时的OOP实际上包含了现在的OOD和OOP两个阶段,这对程序设计者要求比较高,许多人感到很难掌握。

现在设计一个大的软件,是严格按照面向对象软件工程的5个阶段进行的,这个5个阶段的工作不是由一个人从头到尾完成的,而是由不同的人分别完成,这样OOP阶段的任务就比较简单了。程序编写者只需要根据OOd提出的思路,用面向对象语言编写出程序既可。

十五、领域建模

领域模型,顾名思义,就是需求所涉及的领域的一个建模,更通俗的讲法是业务模型。

领域模型是完成从需求分析到面向对象设计的一座桥梁。

发掘重要的业务领域概念;建立业务领域概念之间的关系。

领域建模三字经:找名词、加属性、连关系

猜你喜欢

转载自www.cnblogs.com/tom100/p/10675086.html