一. 三大编程范式
前面学完了Python的基本语法,能写Python代码, 而且可以处理工作中的一些问题,今天开始就要进入面向对象的学习了。首先,了解下三大编程范式,编程范式就是编程方法论,表明的是一种编程风格。
切记:三种编程风格没有好坏之分,有分别的是使用不同风格的人。
1. 面向过程编程
核心是过程二字,过程指的是解决问题的步骤,即先干什么再干什么......面向过程的设计就好比精心设计好一条流水线,是一种机械式的思维方式。
优点是:复杂度的问题流程化,进而简单化(一个复杂的问题,分成一个个小的步骤去实现,实现小的步骤将会非常简单)
缺点是:一套流水线或者流程就是用来解决一个问题,生产汽水的流水线无法生产汽车,即便是能,也得是大改,改一个组件,牵一发而动全身。
应用场景:一旦完成基本很少改变的场景,著名的例子有Linux內核,git,以及Apache HTTP Server等。
2. 函数式编程
包括数学层面的函数和代码层面的函数。
3.面向对象编程
核心是对象二字,(要理解对象为何物,必须把自己当成上帝,上帝眼里世间存在的万物皆为对象,不存在的也可以创造出来。面向对象的程序设计好比如来设计西游记,如来要解决的问题是把经书传给东土大唐,如来想了想解决这个问题需要四个人:唐僧,沙和尚,猪八戒,孙悟空,每个人都有各自的特征和技能(这就是对象的概念,特征和技能分别对应对象的数据属性和方法属性),然而这并不好玩,于是如来又安排了一群妖魔鬼怪,为了防止师徒四人在取经路上被搞死,又安排了一群神仙保驾护航,这些都是对象。然后取经开始,师徒四人与妖魔鬼怪神仙交互着直到最后取得真经。如来根本不会管师徒四人按照什么流程去取),对象是特征与技能的结合体,基于面向对象设计程序就好比在创造一个世界,你就是这个世界的上帝,存在的皆为对象,不存在的也可以创造出来,与面向过程机械式的思维方式形成鲜明对比,面向对象更加注重对现实世界的模拟,是一种“上帝式”的思维方式。
优点:
解决了程序的扩展性。对某一个对象单独修改,会立刻反映到整个体系中,如对游戏中一个人物参数的特征和技能修改都很容易。
缺点:
1. 编程的复杂度远高于面向过程,不了解面向对象而立即上手基于它设计程序,极容易出现过度设计的问题。一些扩展性要求低的场景使用面向对象会徒增编程难度,比如管理linux系统的shell脚本就不适合用面向对象去设计,面向过程反而更加适合。
2. 无法向面向过程的程序设计流水线式的可以很精准的预测问题的处理流程与结果,面向对象的程序一旦开始就由对象之间的交互解决问题,即便是上帝也无法准确地预测最终结果。于是我们经常看到对战类游戏,新增一个游戏人物,在对战的过程中极容易出现阴霸的技能,一刀砍死3个人,这种情况是无法准确预知的,只有对象之间交互才能准确地知道最终的结果。
应用场景:需求经常变化的软件,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方
面向对象的程序设计并不是全部。对于一个软件质量来说,面向对象的程序设计只是用来解决扩展性。
二.编程进化论
1. 编程最开始就是无组织无结构,从简单控制流中按步写指令
2. 从上述的指令中提取重复的代码块或逻辑,组织到一起(比如,定义了一个函数),便实现了代码重用,且代码从无结构走向了结构化,创建程序的过程变得更具逻辑性。
3. 定义函数都是独立于函数外定义变量,然后作为参数传递给函数,这意味着:数据与动作是分离的。
4. 如果把数据和动作内嵌到一个结构(函数或类)里面,那么就有了一个“对象矽统”(对象就是数据与函数整合到一起的产物)。
三. 颠覆一下面向对象
def dog(name,gender,type): # 狗的动作 def jiao(dog): print('一条狗[%s],汪汪汪' % dog['name']) def chi_shi(dog): print('一条[%s] 正在吃屎' % dog['type']) # 定义了init函数,作用是初始化狗,初始化了狗的特征和动作 def init(name,gender,type): dog1 = { # 初始化狗的特征 'name':name, 'gender': gender, 'type': type, # 初始化狗的动作 'jiao':jiao, 'chi_shi':chi_shi, } return dog1 return init(name,gender,type) # d1,d2就是一个个具体的对象,是一条真真实实的狗 d1=dog('xiaohei','母','中华田园犬') d2=dog('heimei','母','藏敖') print(d1) print(d2) d1['jiao'](d1) d2['chi_shi'](d2) # 结果 {'name': 'xiaohei', 'gender': '母', 'type': '中华田园犬', 'jiao': <function dog.<locals>.jiao at 0x00C11B28>, 'chi_shi': <function dog.<locals>.chi_shi at 0x00C11AE0>} {'name': 'heimei', 'gender': '母', 'type': '藏敖', 'jiao': <function dog.<locals>.jiao at 0x00C11A50>, 'chi_shi': <function dog.<locals>.chi_shi at 0x00C11A98>} 一条狗[xiaohei],汪汪汪 一条[藏敖] 正在吃屎
# 如何描述一个对象? # 1.描述它的特征 # 2.描述它的动作 # 所以对象,就是特征与动作的结合 # 如何描述一个类? # 1. 描述他的特征 # 2. 描述他的动作 # 3. 把具有共性的特征和动作提取出来,抽象成一个类,比如人类, 植物等,就是一个类 # 对象就是从类里抽象出的一个具体的个体, 类所具有的全部特征和动作,这个具体的对象都具有。 # 比如某个具体的人,你媳妇,她就具有人类所有的特征和动作。
为什么说是颠覆一下面向对象呢?
因为常规认为面向对象都是class类,而此处却用的是def函数,而且它的设计思想就是面向对象的思想。
三.类与对象
1. 什么是类?
把一类事物的相同的特征和动作整合到一起就是类。类是一个抽象的概念。
2. 什么是对象?
对象就是基于类而创建的一个具体的事物,具体真实的存在。对象也是特征和动作整合到一起的。
3. 那么问题来了,先有的一个个具体存在的对象(比如一个具体存在的人),还是先有的人类这个概念,这个问题需要分两种情况去看
(1)在现实世界中:先有对象,再有类
世界上肯定是先出现各种各样的实际存在的物体,然后随着人类文明的发展,人类站在不同的角度总结出了不同的种类,如人类、动物类、植物类等概念
也就说,对象是具体的存在,而类仅仅只是一个概念,并不真实存在
(2)在程序中:务必保证先定义类,后产生对象
这与函数的使用是类似的,先定义函数,后调用函数,类也是一样的,在程序中需要先定义类,后调用类
不一样的是,调用函数会执行函数体代码返回的是函数体执行的结果,而调用类会产生对象,返回的是对象
好啦,下面看看怎么一步步接近真相的。
# 需求 # 定义一个学校类 # 特征:name(学校名字), addr(学校地址), type(学校类型) # 动作:考试,招生,开除奖励
1 # 原始方法 2 3 # 1. 特征 4 school1 = { 5 'name':'人大', 6 'address':"海淀", 7 'type':'985' 8 } 9 10 # 学校可以完成的动作 11 def kaoshi(school): 12 print("%s 学校正在考试" %school['name']) 13 14 def zhaosheng(shcool): 15 print("%s %s 学校正在招生" %(school('type'),school['name']))
1 # 上面一个基本的学校类就定义好了,但是存在一个问题,就是可能来了一条狗也能调用考试的方法, 2 # 所以需要进化,把属性和动作整合到一起,到目前为止通过作用域的方式可以完成 3 4 def school(): 5 # 1. 特征 6 school1 = { 7 'name': '人大', 8 'address': "海淀", 9 'type': '985' 10 } 11 12 # 学校可以完成的动作 13 def kaoshi(school): 14 print("%s 学校正在考试" % school['name']) 15 16 def zhaosheng(shcool): 17 print("%s %s 学校正在招生" % (school('type'), school['name']))
1 # 这里虽然完成了把属性和动作的整合,保证外部来的那条狗是无法调用了学校了,但是,又出现个新问题, 2 # 就是写死了一个对象school1,有没有什么方法可以实现多个对象呢? 3 4 def school(name,addr,typer): 5 # 1. 特征 6 school1 = { 7 'name': name, # 这里就不能写死了,需要一个变量替代,变量从外部传入 8 'address': addr, 9 'type': typer 10 } 11 12 # 学校可以完成的动作 13 def kaoshi(school): 14 print("%s 学校正在考试" % school['name']) 15 16 def zhaosheng(shcool): 17 print("%s %s 学校正在招生" % (school('type'), school['name']))
1 # 这里虽然完成了把属性和动作的整合,保证外部来的那条狗是无法调用了学校了,但是,又出现个新问题, 2 # 就是写死了一个对象school1,有没有什么方法可以实现多个对象呢? 3 4 def school(name,addr,typer): 5 # 1. 特征 6 school1 = { 7 'name': name, # 这里就不能写死了,需要一个变量替代,变量从外部传入 8 'address': addr, 9 'type': typer 10 } 11 12 # 学校可以完成的动作 13 def kaoshi(school): 14 print("%s 学校正在考试" % school['name']) 15 16 def zhaosheng(shcool): 17 print("%s %s 学校正在招生" % (school('type'), school['name']))
1 1 def school(name,addr,typer): 2 2 3 3 # 学校可以完成的动作 4 4 def kaoshi(school): 5 5 print("%s 学校正在考试" % school['name']) 6 6 7 7 def zhaosheng(school): 8 8 print("%s %s 学校正在招生" % (school['type'], school['name'])) 9 9 10 10 # 1. 特征 11 11 sch = { 12 12 'name': name, # 这里就不能写死了,需要一个变量替代,变量从外部传入 13 13 'address': addr, 14 14 'type': typer, 15 15 # 在特征里实现对象和动作的绑定 16 16 'kao_shi':kaoshi, 17 17 'zhao_sheng':zhaosheng 18 18 } 19 19 # 对象生成了,需要返回才可以 20 20 return sch 21 21 22 22 # 生成对象s1人大 23 23 s1 = school('人大','海淀','985') 24 24 print(s1) 25 25 # 返回的是字典 26 26 # {'name': '人大', 'address': '海淀', 'type': '985', 'kao_shi': <function school.<locals>.kaoshi at 0x007E1AE0>, 'zhao_sheng': <function school.<locals>.zhaosheng at 0x007E1A50>} 27 27 28 28 # 可以通过生成的对象来调学校的名字了 29 29 print(s1['name']) 30 30 # 人大 31 31 32 32 # 可以看这个学校有什么动作 33 33 print(s1['kao_shi']) # <function school.<locals>.kaoshi at 0x003F1AE0> 34 34 35 35 print(s1['zhao_sheng']) # <function school.<locals>.zhaosheng at 0x003F1A50> 36 36 37 37 38 38 # 如果想让新生成的s1人大这个对象来执行学校的考试动作 39 39 s1['kao_shi'](s1) 40 40 # 人大 学校正在考试 41 41 42 42 # 如果想让新生成的s1人大这个对象来执行学校的招生动作 43 43 s1['zhao_sheng'](s1) 44 44 # 985 人大 学校正在招生
''' 上面的程序已经可以完成功能了,但是还是不够好, ''' def school(name,addr,typer): # 让他变的更好,要在局部作用域里,定义一个init的函数 def init(name,addr,typer): # 1. 特征 sch = { 'name': name, # 这里就不能写死了,需要一个变量替代,变量从外部传入 'address': addr, 'type': typer, # 在特征里实现对象和动作的绑定 'kao_shi': kaoshi, 'zhao_sheng': zhaosheng } # 对象生成了,需要返回才可以 return sch # 学校可以完成的动作 def kaoshi(school): print("%s 学校正在考试" % school['name']) def zhaosheng(school): print("%s %s 学校正在招生" % (school['type'], school['name'])) # 先 看下结构,代码就变得整洁了
# 但是,又有 一个问题,执行school函数,函数体内没有任何运行,而我们的目的是一执行school函数,立马回返回一个真实存在的s1的对象, # 那怎么才能生成这个对象呢?而做这件事的就是init函数,所以只需要执行school函数的时候,运行init函数就行了呗。所以加上init(),另外, # 函数运行,需要返回结果哦,所以是 return init() def school(name,addr,typer): # 让他变的更好,要在局部作用域里,定义一个init的函数 def init(name,addr,typer): # 1. 特征 sch = { 'name': name, # 这里就不能写死了,需要一个变量替代,变量从外部传入 'address': addr, 'type': typer, # 在特征里实现对象和动作的绑定 'kao_shi': kaoshi, 'zhao_sheng': zhaosheng } # 对象生成了,需要返回才可以 return sch # 学校可以完成的动作 def kaoshi(school): print("%s 学校正在考试" % school['name']) def zhaosheng(school): print("%s %s 学校正在招生" % (school['type'], school['name'])) # 运行init()函数,并返回运行结果 return init(name,addr,typer) s1 = school('清华','五道口','公立') print(s1) s2 = school('人大','魏公村','公立') print(s2) s2['zhao_sheng'](s2)
上面的实现过程就是一个面向对象的设计过程思想
四. 面向对象设计(Object oriented design)
所谓面向对象设计:就是将一类事物的具体数据和动作整合到一起,即面向对象设计
面向对象设计(OOD)不会特别要求面向对象编程语言。事实上,OOD可以由纯结构化语言来实现(比如C)。但如果想要构造局对对象性质和特点的数据类型,就需要在程序上作更多努力。
五. 面向对象编程(object-oriented programming)
所谓面向对象编程:就是定义类+实例/对象的方法去实现面向对象的设计
# 用面向对象编程独有的语法class去实现面向对象设计 # 最直观的展示面向对象展示
''' 这段程序里有两个地方要注意:第一个是class,第二个是方法后面的self,这个self到底是什么东东?
'''
关于self,其实说白了就是对象自己。
对于函数来说,就是一个位置参数,函数在运行的时间,需要把自己传进来。
关于self,其实说白了就是对象自己。 对于函数来说,就是一个位置参数,函数在运行的时间,需要把自己传进来。 用上面的示例可以清楚的看到 def school(name,addr,typer): def init(name,addr,typer): sch = { 'name': name, 'address': addr, 'type': typer, 'kao_shi': kaoshi, 'zhao_sheng': zhaosheng } return sch def kaoshi(school): print("%s 学校正在考试" % school['name']) def zhaosheng(school): print("%s %s 学校正在招生" % (school['type'], school['name'])) # 替换成self后,原理就是这样。 def school(name,addr,typer): def init(name,addr,typer): sch = { 'name': name, 'address': addr, 'type': typer, 'kao_shi': kaoshi, 'zhao_sheng': zhaosheng } return sch def kaoshi(self): print("%s 学校正在考试" % self['name']) def zhaosheng(self): print("%s %s 学校正在招生" % (self['type'], self['name']))
小总结
1. 有人说,只有class定义类才是面向对象,def定义函数就是函数相关的,跟面向对象没关系----错的哦,上面已经很清楚的通过def展示了面向对象。
2. 一门面向对象语言不一定会强制你写面向对象(OO)方面的程序。例如,C++可以被认为“更好的C”;而java则要求万物皆类,此外,还规定,一个源文件对应一个类定义。
3.然而,在Python中,类和面向对象编程OOP都不是日常编程所必须的。尽管它从一开始设计就是面向对象的,并且结构上支持OOP,但Python没有限定或要求你在你的应用中写OO的代码。
Python是面向对象语言,提供了丰富的class方面的内容。
4. 用面向对象语言写程序,和一个程序的设计是面向对象的,两者是八竿子打不着的两码事。