17.1、面向过程的程序设计:
1、核心是过程二字,过程指的是解决问题的步骤,即先干什么再干什么......面向过程的设计就好比精心设计好一条流水线,是一种机械式的思维方式。
2、优点是:复杂度的问题流程化,进而简单化(一个复杂的问题,分成一个个小的步骤去实现,实现小的步骤将会非常简单);
3、缺点是:一套流水线或者流程就是用来解决一个问题,生产汽水的流水线无法生产汽车,即便是能,也得是大改,改一个组件,牵一发而动全身。
4、应用场景:一旦完成基本很少改变的场景,著名的例子有Linux內核,git,以及Apache HTTP Server等。
17.2、面向对象的程序设计:
1、核心是对象二字,对象是特征与技能的结合体,特征和技能分别对应对象的数据属性和方法属性,基于面向对象设计程序就好比在创造一个世界,
你就是这个世界的上帝,存在的皆为对象,不存在的也可以创造出来,与面向过程机械式的思维方式形成鲜明对比,面向对象更加注重对现实世界的
模拟,是一种“上帝式”的思维方式;
2、优点是:解决了程序的扩展性。对某一个对象单独修改,会立刻反映到整个体系中,如对游戏中一个人物参数的特征和技能修改都很容易。
3、缺点:
(1)编程的复杂度远高于面向过程,不了解面向对象而立即上手基于它设计程序,极容易出现过度设计的问题。一些扩展性要求低的场景使用面向对象
会徒增编程难度,比如管理linux系统的shell脚本就不适合用面向对象去设计,面向过程反而更加适合。
(2)无法向面向过程的程序设计流水线式的可以很精准的预测问题的处理流程与结果,面向对象的程序一旦开始就由对象之间的交互解决问题,即便是
上帝也无法准确地预测最终结果。于是我们经常看到对战类游戏,新增一个游戏人物,在对战的过程中极容易出现阴霸的技能,一刀砍死3个人,这种情
况是无法准确预知的,只有对象之间交互才能准确地知道最终的结果。
4、应用场景:需求经常变化的软件,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方,
面向对象的程序设计并不是全部。对于一个软件质量来说,面向对象的程序设计只是用来解决扩展性。
17.3、类与对象:
1、类即类别、种类,是面向对象设计最重要的概念,对象是特征与技能的结合体,而类则是一系列对象相似的特征与技能的结合体,那么问题来了,先
有的一个个具体存在的对象(比如一个具体存在的人),还是先有的人类这个概念,这个问题需要分两种情况去看:
(1)在现实世界中:先有对象,再有类:
世界上肯定是先出现各种各样的实际存在的物体,然后随着人类文明的发展,人类站在不同的角度总结出了不同的种类,如人类、动物类、植物类等概念,
也就说,对象是具体的存在,而类仅仅只是一个概念,并不真实存在;
(2)在程序中:务必保证先定义类,后产生对象;
2、类与函数的使用是类似的,先定义函数,后调用函数,类也是一样的,在程序中需要先定义类,后调用类,不一样的是,调用函数会执行函数体代码返
回的是函数体执行的结果,而调用类会产生对象,返回的是对象;
3、在程序中特征用变量标识,技能用函数标识,因而类中最常见的无非是变量和函数的定义;
4、注意:
(1)类中可以有任意python代码,这些代码在类定义阶段便会执行,因而会产生新的名称空间,用来存放类的变量名与函数名,可以通过<类名>.__dict__查看;
(2)python为我们提供专门的.语法,点是访问属性的语法,类中定义的名字,都是类的属性;
(3)类只有数据属性和函数属性,使用<类名>.<属性名>的方式进行访问,数据属性是所有对象共享的,函数属性是绑定给对象用的;对象只有数据
属性,通过<对象名>.<属性名>的方式进行访问,如果访问不到则访问类的数据属性或函数属性,但是不能修改类的数据属性或函数属性,对象在生成
或调用类的函数属性时默认会将对象自身(self,对象的数据属性字典)传入;
个人理解:python面向对象的实质是每个对象都在维护自己初始化数据属性字典,共享类字典中的数据属性并绑定类字典中的函数属性,但是不能修改类的数据
属性或函数属性;
说明:在定义类的函数属性时最好不要和对象的数据属性重复,否则在用对象调用类的函数属性时会报错(原因是对象在自己的字典中找到了同名的数据属性,不会往
类中找同名的函数属性,所以当以函数属性的方式调用时就会报错);
(4)实例对象示范:
1)面向对象的程序设计:
def dog(name,gender,type):
# 狗的动作
def jiao(dog):
print('一条狗%s,汪汪汪' % dog['name'])
def chi_shi(dog):
print('一条[%s] 正在吃饭' % dog['type'])
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=dog('旺财','母','中华田园犬')
d2=dog('大黄','母','藏敖')
print(d1)
print(d2)
d1['jiao'](d1)
d2['chi_shi'](d2)
2)python中面向对象语法:
class Chinese:
country = 'China'
def __init__(self, name):
self.name = name
#该方法是在对象产生之后才会执行,只用来为对象进行初始化操作,可以有任意代码,但一定不能有返回值
#默认将对象的数据属性以字典的形式进行返回
def play_ball(self, ball):
print('%s 正在打' % self.name, ball)
#self,对象的数据属性字典
#最后,默认执行__init__函数对对象进行初始化,并将对象的数据属性字典返回
p1 = Chinese('lc1')
p1.play_ball("篮球")
p2 = Chinese('lc2')
p2.play_ball("羽毛球")
5、程序中类的用法:
(1). :专门用来访问属性,本质操作的就是__dict__;
(2)查看类的属性:
<类名>.<属性名> #等于经典类的操作<类名>.__dict__['<属性名>']
(3)增加或改变类的属性:
<类名>.<属性名>='<值>' #等于经典类的操作<类名>.__dict__['<属性名>']='<值>'
(4)删除类的属性:
del <类名>.<属性名> #等于经典类的操作<类名>.__dict__.pop('<属性名>')
6、程序中对象的用法:
(1)调用类,或称为实例化,得到对象:
<对象名>=<实例名>()
(2). :专门用来访问属性,本质操作的就是__dict__;
(3)查看对象的属性:
<对象名>.<属性名> #等于经典类的操作<对象名>.__dict__['<属性名>']
(4)增加或改变对象的属性:
<对象名>.<属性名>='<值>' #等于经典类的操作<对象名>.__dict__['<属性名>']='<值>'
(5)删除对象的属性:
del <对象名>.<属性名> #等于经典类的操作<类名>.__dict__.pop('<属性名>')
7、python为类内置的特殊属性:
<类名>.__name__ # 类的名字(字符串)
<类名>.__doc__ # 类的文档字符串
<类名>.__base__ # 类的第一个父类(继承)
<类名>.__bases__ # 类所有父类构成的元组(继承)
<类名>.__dict__ # 类的字典属性
<类名>.__module__ # 类定义所在的模块
<类名>.__class__ # 实例对应的类(新式类)
!!!补充说明:从代码级别看面向对象 !!!
8、为什么要使用类:
(1)在没有学习类这个概念时,数据与功能是分离的:
def exc1(host,port,db,charset,sql):
conn=connect(host,port,db,charset)
return conn.execute(sql)
def exc2(host,port,db,charset,proc_name)
conn=connect(host,port,db,charset)
return conn.call_proc(proc_name)
exc1('127.0.0.1',3306,'db1','utf8','select * from tb1;')
exc2('127.0.0.1',3306,'db1','utf8','存储过程的名字')
#缺点:每次调用都需要重复传入一堆参数
(2)我们能想到的解决方法是,把这些变量都定义成全局变量:
HOST='127.0.0.1'
PORT='3306'
DB=‘db1’
CHARSET='utf8'
def exc1(host,port,db,charset,sql):
conn=connect(host,port,db,charset)
return conn.execute(sql)
def exc2(host,port,db,charset,proc_name)
conn=connect(host,port,db,charset)
return conn.call_proc(proc_name)
exc1(HOST,PORT,DB,CHARSET,'select * from tb1;')
exc2(HOST,PORT,DB,CHARSET,'存储过程的名字')
#缺点:我们将会定义一大堆全局变量,这些全局变量并没有做任何区分,即能够被所有功能使用,然而事实上只有
HOST,PORT,DB,CHARSET是给exc1和exc2这两个功能用的。
(3)使用类:
class MySQLHandler:
def __init__(self,host,port,db,charset='utf8'):
self.host=host
self.port=port
self.db=db
self.charset=charset
self.conn=connect(self.host,self.port,self.db,self.charset)
def exc1(self,sql):
return self.conn.execute(sql)
def exc2(self,proc_name):
return self.conn.call_proc(sql)
obj=MySQLHandler('127.0.0.1',3306,'db1')
obj.exc1('select * from tb1;')
obj.exc2('存储过程的名字')
9、类的数据属性是所有对象共享的,id都一样:
print(id(Chinese.country))
print(id(p1.country))
print(id(p2.country))
"""
输出结果:
1957293549136
1957293549136
1957293549136
"""
10、类的函数属性是绑定给对象使用的,obj.method称为绑定方法,内存地址都不一样:
提示:id是python的实现机制,并不能真实反映内存地址,如果有内存地址,还是以内存地址为准;
print(Chinese.play_ball)
print(p1.play_ball)
print(p2.play_ball)
'''
<function Chinese.play_ball at 0x0000020149DDBA60>
<bound method Chinese.play_ball of <__main__.Chinese object at 0x0000020149DFFF60>>
<bound method Chinese.play_ball of <__main__.Chinese object at 0x0000020149E044E0>>
'''
17.4、对象之间的交互:
1、定义盖伦类:
class Garen: #定义英雄盖伦的类,不同的玩家可以用它实例出自己英雄;
camp='Demacia' #所有玩家的英雄(盖伦)的阵营都是Demacia;
def __init__(self,nickname,aggressivity=58,life_value=455): #英雄初始攻击力和生命值;
self.nickname=nickname #为自己的盖伦起个别名;
self.aggressivity=aggressivity #英雄都有自己的攻击力;
self.life_value=life_value #英雄都有自己的生命值;
def attack(self,enemy): #普通攻击技能,enemy是敌人;
enemy.life_value-=self.aggressivity #根据自己的攻击力,攻击敌人就减掉敌人的生命值;
2、创建一个Riven类:
class Riven: #定义英雄瑞文的类,不同的玩家可以用它实例出自己英雄;
camp='Noxus' #所有玩家的英雄(锐雯)的阵营都是Noxus;
def __init__(self,nickname,aggressivity=54,life_value=414): #英雄初始攻击力和生命值;
self.nickname=nickname #为自己的锐雯起个别名;
self.aggressivity=aggressivity #英雄都有自己的攻击力;
self.life_value=life_value #英雄都有自己的生命值;
def attack(self,enemy): #普通攻击技能,enemy是敌人;
enemy.life_value-=self.aggressivity #根据自己的攻击力,攻击敌人就减掉敌人的生命值。
3、实例出俩英雄:
g1 = Garen('草丛伦')
r1 = Riven('锐雯雯')
print(g1.life_value)
r1.attack(g1)
# 交互:锐雯雯攻击草丛伦,反之一样
print(g1.life_value)
"""
#输出结果:
455
401
"""
补充:
garen_hero.Q()称为向garen_hero这个对象发送了一条消息,让他去执行Q这个功能,类似的有:
garen_hero.W()
garen_hero.E()
garen_hero.R()
17.5、面向对象小白容易犯的错误:
1、面向对象的程序设计看起来高大上,所以我在编程时就应该保证通篇class,这样写出的程序一定是好的程序(面向对象只适合那些可扩展性要求比较高的场景)。
2、很多人喜欢说面向对象三大特性(这是从哪传出来的,封装,多态,继承?漏洞太多太多,好吧暂且称为三大特性),那么我在基于面向对象编程时,我一定要让
我定义的类中完整的包含这三种特性,这样写肯定是好的程序。好家伙,我说降龙十八掌有十八掌,那么你每次跟人干仗都要从第一掌打到第18掌这才显得你会了是么,
面对敌人,你打到第三掌对方就已经倒下了,你说,不行,你给老子起来,老子还没有show完......。
3、类有类属性,实例有实例属性,所以我们在定义class时一定要定义出那么几个类属性,想不到怎么办,那就使劲的想,定义的越多越牛逼,这就犯了一个严重的错
误,程序越早面向对象,死的越早,为啥面向对象,因为我们要将数据与功能结合到一起,程序整体的结构都没有出来,或者说需要考虑的问题你都没有搞清楚个八九
不离十,你就开始面向对象了,这就导致了你在那里干想,自以为想通了,定义了一堆属性,结果后来又都用不到,或者想不通到底应该定义啥,那就一直想吧,想着
想着就疯了。你见过哪家公司要开发一个软件,上来就开始写,肯定是频繁的开会讨论计划。