面向对象的由来
什么是面向对象的程序设计及为什么要有它
面向过程的设计
概念
核心是过程二字,过程指的是解决问题的步骤,即先干什么再干什么......面向过程的设计就好比精心设计好一条流水线,是一种机械式的思维方式。若程序一开始是要着手解决一个大的问题,面向过程的基本设计思路就是把这个大的问题分解成很多小的问题或子过程,这些子过程在执行的过程中继续分解,直到小问题足够简单到可以在一个小步骤范围内解决。
优点
复杂度的问题流程化,进而简单化(一个复杂的问题,分成一个个小的步骤去实现,实现小的步骤将会非常简单)
缺点
一套流水线或者流程就是用来解决一个问题,生产汽水的流水线无法生产汽车,即便是能,也得是大改,改一个组件,牵一发而动全身,扩展性极差。
应用场景
面向过程的程序设计思想一般用于那些功能一旦实现之后就很少需要改变的场景。如果你只是写一些简单的脚本,去做一些一次性的任务,用面向过程的方式是极好的。一旦完成基本很少改变的场景,著名的例子有Linux內核,git,以及Apache HTTP Server等,但是如果你要处理任务是复杂的,且需要不断迭代和维护的,那还是用
面向对象最方便。
面向对象的程序设计
概念
核心是对象二字,(要理解对象为何物,必须把自己当成上帝,上帝眼里世间存在的万物皆为对象,不存在的也可以创造出来。面向对象的程序设计好比如来设计西游记,如来要解决的问题是把经书传给东土大唐,如来想了想解决这个问题需要四个人:唐僧,沙和尚,猪八戒,孙悟空,每个人都有各自的特征和技能(这就是对象的概念,特征和技能分别对应对象的数据属性和方法属性),然而这并不好玩,于是如来又安排了一群妖魔鬼怪,为了防止师徒四人在取经路上被搞死,又安排了一群神仙保驾护航,这些都是对象。然后取经开始,师徒四人与妖魔鬼怪神仙交互着直到最后取得真经。如来根本不会管师徒四人按照什么流程去取),对象是特征与技能的结合体,基于面向对象设计程序就好比在创造一个世界,你就是这个世界的上帝,存在的皆为对象,不存在的也可以创造出来,与面向过程机械式的思维方式形成鲜明对比,面向对象更加注重对现实世界的模拟,是一种“上帝式”的思维方式。
优点
解决了程序的扩展性低的问题。需要强调的是,对一个软件质量来说,面向对象的程序设计并不代表全部。面向对象的程序设计只是用来解决扩展性问题。
缺点
1、编程的复杂度远高于面向过程,不了解面向对象而立即上手基于它设计程序,极容易出现过度设计的问题。一些扩展性要求低的场景使用面向对象会徒增编程难度,比如管理linux系统的shell脚本就不适合用面向对象去设计,面向过程反而更加适合。
2、无法向面向过程的程序设计流水线式的可以很精准的预测问题的处理流程与结果,面向对象的程序一旦开始就由对象之间的交互解决问题,即便是上帝也无法准确地预测最终结果。于是我们经常看到对战类游戏,新增一个游戏人物,在对战的过程中极容易出现阴霸的技能,一刀砍死3个人,这种情况是无法准确预知的,只有对象之间交互才能准确地知道最终的结果。
应用场景
需求经常变化的软件,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方面向对象的程序设计并不是全部。对于一个软件质量来说,面向对象的程序设计只是用来解决扩展性。
使用函数进行面向对象设计
函数式面向对象设计
#方案一
def dog(name,gender,type):
# 狗的动作
def jiao(dog):
print(\'一条狗[%s],汪汪汪\' % dog[\'name\'])
def chi_shi(dog):
print(\'一条[%s] 正在吃屎\' % dog[\'type\'])
#狗的特征
dog1 = {
\'name\':name,
\'gender\': gender,
\'type\': type,
\'jiao\':jiao,
\'chi_shi\':chi_shi,
}
return dog1
d1=dog(\'wj\',\'母\',\'中华田园犬\')
d2=dog(\'qf\',\'公\',\'藏敖\')
print(d1)
print(d2)
d1[\'jiao\'](d1)
d2[\'chi_shi\'](d2)
#方案二(改进)
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(\'wj\',\'母\',\'中华田园犬\')
d2=dog(\'qf\',\'公\',\'藏敖\')
print(d1)
print(d2)
d1[\'jiao\'](d1)
d2[\'chi_shi\'](d2)
面向对象设计练习
def school(name,addr,type):
def init(name, addr, type):
sch = {
\'name\': name,
\'addr\': addr,
\'type\': type,
\'kao_shi\': kao_shi,
\'zhao_sheng\': zhao_sheng,
}
return sch
def kao_shi(school):
print(\'%s 学校正在考试\' %school[\'name\'])
def zhao_sheng(school):
print(\'%s %s 正在招生\' %(school[\'type\'],school[\'name\']))
return init(name,addr,type)
s1=school(\'文天\',\'郑蒲岗\',\'公立学校\')
print(s1)
print(s1[\'name\'])
s1[\'zhao_sheng\'](s1)
s2=school(\'清华\',\'北京\',\'公立学校\')
print(s2)
print(s2[\'name\'],s2[\'addr\'],s2[\'type\'])
s2[\'zhao_sheng\'](s2)
类与对象(使用class进行面向对象设计)
类与对象的概念
类就是类别、种类,是面向对象设计最重要的概念,从之前我们得知对象是特征与技能的结合体,而类则是一系列对象相似的特征和技能的结合体。
那么问题来了,是先有一个个具体存在的对象(比如一个具体存在的人)?还是先有人这个概念?这个问题需要分两种情况去看
在现实世界中:肯定是先有对象,再有类
世界上肯定是先出现各种各样的实际存在的物体,然后随着人类文明的发展,人类站在不同的角度总结出了不同的种类,比如人类、动物类、植物类等概念,也就是说,对象是具体存在的,而类仅仅是一个概念,并不真实存在,比如你无法告诉我人类具体是指哪一个人。
在程序中:务必保证先定义类,后产生对象
这与函数的使用是类似的:先定义函数,后调用函数。
类也是一样的:在程序中需要先定义类。后调用类。
不一样的是:调用函数会执行函数体代码,返回的是函数体执行的结果。而调用类会产生对象,返回的是对象。
定义类(现实世界、程序世界)
按照上述步骤,我们来定义一个类(我们站在学校的角度去看,在座的各位都是学生)
在现实世界中:肯定是先有对象,再有类
在现实世界中,站在文天学校的角度:先有对象,再有类
现实对象
对象1:李易峰
特征:
学校=文天
姓名=李易峰
性别=男
年龄=23
技能:
学习
吃饭
睡觉
对象2:勇虹王者
特征:
学校=文天
姓名=勇虹王者
性别=男
年龄=28
技能:
学习
吃饭
睡觉
对象3:秋名山
特征:
学校=文天
姓名=秋名山
性别=男
年龄=18
技能:
学习
吃饭
睡觉
现实类
现实中的学生类
相似的特征:
学校=文天
相似的技能:
学习
吃饭
睡觉
在程序中:务必保证先定义(类),后使用类(用来产生对象)
在Python程序中的
类用class关键字定义,而在程序中特征用变量标识,技能用函数标识,因而类中最常见的无非是:变量和函数的定义。
程序中的类
class WentianStudent:
school=\'文天\'
def learn(self):
print(\'is learning\')
def eat(self):
print(\'is eating\')
def sleep(self):
print(\'is sleeping\')
#注意
#1.类中可以有任意python代码,这些代码在类定义阶段便会执行,因而会产生新的名称空间,用来存放类的变量名与函数名,可以通过WentianStudent.__dict__查看
#2.类中定义的名字,都是类的属性,点是访问属性的语法
#3.对于经典类来说我们可以通过该字典操作类名称空间的名字(新式类有限制),但python为我们提供专门的语法
程序中的对象
调用类,或称为实例化,得到对象
s1=WentianStudent()
s2=WentianStudent()
s3=WentianStudent()
#如此,s1、s2、s3都一样了,而这三者除了相似的属性之外还各种不同的属性,这就用到了__init__
\'\'\'
__init__方法:
__init__方法是在对象产生之后才会执行,只用来为对象进行初始化操作
可以有任意代码,但一定不能有返回值(可以返回None,但是没有意义)
\'\'\'
class WentianStudent:
......
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex=sex
......
#先调用类产生空对象s1,然后调用OldboyStudent.__init__(s1,\'李易峰\',\'男\',23)
s1=WentianStudent(\'李易峰\',\'男\',23)
s2=WentianStudent(\'勇虹王者\',\'男\',28)
s3=WentianStudent(\'秋名山\',\'男\',18)
程序中类与对象的用法
程序中类的用法
.:专门用来访问属性,本质操作的就是__dict__
WentianStudent.school #等于经典类的操作WentianStudent.__dict__[\'school\']
WentianStudent.school=\'文天\' #等于经典类的操作WentianStudent.__dict__[\'school\']=\'文天\'
WentianStudent.x=1 #等于经典类的操作WentianStudent.__dict__[\'x\']=1
del WentianStudent.x #等于经典类的操作WentianStudent.__dict__.pop(\'x\')
程序中对象的用法
#执行__init__,s1.name=\'秋名山\',很明显也会产生对象的名称空间
s2.__dict__
{\'name\': \'勇虹王者\', \'age\': \'男\', \'sex\': 28}
s2.name #s2.__dict__[\'name\']
s2.name=\'勇虹王者\' #s2.__dict__[\'name\']=\'勇虹王者\'
s2.course=\'python\' #s2.__dict__[\'course\']=\'python\'
del s2.course #s2.__dict__.pop(\'course\')
详细解说初始化__init__方法
#方式一、为对象初始化自己独有的特征
class People:
country=\'China\'
x=1
def run(self):
print(\'----->\', self)
# 实例化出三个空对象
obj1=People()
obj2=People()
obj3=People()
# 为对象定制自己独有的特征
obj1.name=\'雯婧\'
obj1.age=18
obj1.sex=\'female\'
obj2.name=\'樵夫\'
obj2.age=28
obj2.sex=\'male\'
obj3.name=\'书呆子\'
obj3.age=28
obj3.sex=\'male\'
# print(obj1.__dict__)
# print(obj2.__dict__)
# print(obj3.__dict__)
# print(People.__dict__)
#方式二、为对象初始化自己独有的特征
class People:
country=\'China\'
x=1
def run(self):
print(\'----->\', self)
# 实例化出三个空对象
obj1=People()
obj2=People()
obj3=People()
# 为对象定制自己独有的特征
def chu_shi_hua(obj, x, y, z): #obj=obj1,x=\'雯婧\',y=18,z=\'female\'
obj.name = x
obj.age = y
obj.sex = z
chu_shi_hua(obj1,\'雯婧\',18,\'female\')
chu_shi_hua(obj2,\'樵夫\',28,\'male\')
chu_shi_hua(obj3,\'书呆子\',28,\'male\')
#方式三、为对象初始化自己独有的特征
class People:
country=\'China\'
x=1
def chu_shi_hua(obj, x, y, z): #obj=obj1,x=\'雯婧\',y=18,z=\'female\'
obj.name = x
obj.age = y
obj.sex = z
def run(self):
print(\'----->\', self)
obj1=People()
# print(People.chu_shi_hua)
People.chu_shi_hua(obj1,\'雯婧\',18,\'female\')
obj2=People()
People.chu_shi_hua(obj2,\'樵夫\',28,\'male\')
obj3=People()
People.chu_shi_hua(obj3,\'书呆子\',28,\'male\')
# 方式四、为对象初始化自己独有的特征
class People:
country=\'China\'
x=1
def __init__(obj, x, y, z): #obj=obj1,x=\'雯婧\',y=18,z=\'female\'
obj.name = x
obj.age = y
obj.sex = z
def run(self):
print(\'----->\', self)
obj1=People(\'雯婧\',18,\'female\') #People.__init__(obj1,\'雯婧\',18,\'female\')
obj2=People(\'樵夫\',28,\'male\') #People.__init__(obj2,\'樵夫\',28,\'male\')
obj3=People(\'书呆子\',28,\'male\') #People.__init__(obj3,\'书呆子\',28,\'male\')
# __init__方法
\'\'\'
强调:
1、该方法内可以有任意的python代码
2、一定不能有返回值
\'\'\'
class People:
country=\'China\'
x=1
def __init__(obj, name, age, sex): #obj=obj1,x=\'雯婧\',y=18,z=\'female\'
# if type(name) is not str:
# raise TypeError(\'名字必须是字符串类型\')
obj.name = name
obj.age = age
obj.sex = sex
def run(self):
print(\'----->\', self)
# obj1=People(\'雯婧\',18,\'female\')
obj1=People(3537,18,\'male\')
# print(obj1.run)
# obj1.run() #People.run(obj1)
# print(People.run)
类的注意事项
1.站的角度不同,定义出的类是截然不同的,详见面向对象实战之需求分析
2.现实中的类并不完全等于程序中的类,比如现实中的公司类,在程序中有时需要拆分成部门类,业务类......
3.有时为了编程需求,程序中也可能会定义现实中不存在的类,比如策略类,现实中并不存在,但是在程序中却是一个很常见的类
自动传值self
#改写
class WentianStudent:
school=\'文天\'
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex=sex
def learn(self):
print(\'%s is learning\' %self.name) #新增self.name
def eat(self):
print(\'%s is eating\' %self.name)
def sleep(self):
print(\'%s is sleeping\' %self.name)
s1=OldboyStudent(\'李易峰\',\'男\',23)
s2=OldboyStudent(\'勇虹王者\',\'男\',28)
s3=OldboyStudent(\'秋明山\',\'男\',18)
类中定义的函数(没有被任何装饰器装饰的)是类的函数属性,类可以使用,但必须遵循函数的参数规则,有几个参数需要传几个参数
WentianStudent.learn(s1) #李易峰 is learning
WentianStudent.learn(s2) #勇虹王者 is learning
WentianStudent.learn(s3) #秋名山 is learning
类中定义的函数(没有被任何装饰器装饰的),其实主要是给对象使用的,而且是绑定到对象的,虽然所有对象指向的都是相同的功能,但是绑定到不同的对象就是不同的绑定方法
强调:绑定到对象的方法的特殊之处在于,绑定给谁就由谁来调用,谁来调用,就会将‘谁’本身当做第一个参数传给方法,即自动传值(方法__init__也是一样的道理)
s1.learn() #等同于WentianStudent.learn(s1)
s2.learn() #等同于WentianStudent.learn(s2)
s3.learn() #等同于WentianStudent.learn(s3)
注意:绑定到对象的方法的这种自动传值的特征,决定了在类中定义的函数都要默认写一个参数self,self可以是任意名字,但是约定俗成地写出self
类即类型
- python的class术语与c++有一定区别,与 Modula-3更像。
- python中一切皆为对象,且python3中类与类型是一个概念,类型就是类
#类型dict就是类dict
>>> list
<class \'list\'>
#实例化的到3个对象l1,l2,l3
>>> l1=list()
>>> l2=list()
>>> l3=list()
#三个对象都有绑定方法append,是相同的功能,但内存地址不同
>>> l1.append
<built-in method append of list object at 0x10b482b48>
>>> l2.append
<built-in method append of list object at 0x10b482b88>
>>> l3.append
<built-in method append of list object at 0x10b482bc8>
#操作绑定方法l1.append(3),就是在往l1添加3,绝对不会将3添加到l2或l3
>>> l1.append(3)
>>> l1
[3]
>>> l2
[]
>>> l3
[]
#调用类list.append(l3,111)等同于l3.append(111)
>>> list.append(l3,111) #l3.append(111)
>>> l3
[111]
python为类内置的特殊属性
类名.__name__# 类的名字(字符串)
类名.__doc__# 类的文档字符串
类名.__base__# 类的第一个父类(在讲继承时会讲)
类名.__bases__# 类所有父类构成的元组(在讲继承时会讲)
类名.__dict__# 类的字典属性
类名.__module__# 类定义所在的模块
类名.__class__# 实例对应的类(仅新式类中)
类与对象属性查找
类的属性
类的数据属性
class WentianStudent:
school=\'文天\'
def learn(self):
print(\'is learning\')
def eat(self):
print(\'is eating\')
def sleep(self):
print(\'is sleeping\')
s1=WentianStudent(\'李易峰\',\'男\',23)
s2=WentianStudent(\'勇虹王者\',\'男\',28)
s3=WentianStudent(\'秋名山\',\'男\',18)
print(id(WentianStudent.school))
print(id(s1.school))
print(id(s2.school))
print(id(s3.school))
\'\'\'
4377347328
4377347328
4377347328
4377347328
\'\'\'
#类的数据属性是类和所有对象共享的,id都一样
#ps:id是python的实现机制,并不能真实反映内存地址,如果有内存地址,还是以内存地址为准
类的函数属性
#类的函数属性是绑定给对象使用的,obj.method称为绑定方法,内存地址都不一样
print(WentianStudent.learn)
print(s1.learn)
print(s2.learn)
print(s3.learn)
\'\'\'
<function WentianStudent.learn at 0x1021329d8>
<bound method WentianStudent.learn of <__main__.WentianStudent object at 0x1021466d8>>
<bound method WentianStudent.learn of <__main__.WentianStudent object at 0x102146710>>
<bound method WentianStudent.learn of <__main__.WentianStudent object at 0x102146748>>
\'\'\'
对象的属性(只有数据属性)
对象的数据属性
\'\'\'
在obj.name会先从obj自己的名称空间里找name,找不到则去类中找,
类也找不到就找父类...最后都找不到(不会找出类所在的定义域)就抛出异常
\'\'\'
class People:
country=\'China\'
x=1
def __init__(obj, name, age, sex):
obj.name = name
obj.age = age
obj.sex = sex
def run(obj):
print("%d岁的%s%s在嗡嗡嗡的叫!"%(obj.age,obj.sex,obj.name))
obj=People(\'蚊子\',18,\'母\')
print(obj.name)
print(obj.x)
print(obj.country)
obj.run()
print(obj.__dict__)
#蚊子
#1
#China
#18岁的母蚊子在嗡嗡嗡的叫!
#{"name":"蚊子","age":18,"sex":"母"}
# __dict__字典中没有函数属性,所以会发现其实是调用类的函数属性,类的函数属性绑定到调用的对象的函数属性上的
类与对象属性(增删改查)
类属性操作
class Chinese:
country=\'China\'
def __init__(self,name):
self.name=name
def play_ball(self,ball):
print(\'%s 正在打 %s\' %(self.name))
查看
print(Chinese.country)
修改
Chinese.country=\'Japan\'
print(Chinese.country)
p1=Chinese(\'qf\')
print(p1.__dict__)
print(p1.country)
增加
Chinese.dang=\'dang\'
# print(Chinese.dang)
# print(p1.dang)
删除
del Chinese.dang
del Chinese.country
print(Chinese.__dict__)
# print(Chinese.country)
def eat_food(self,food):
print(\'%s 正在吃%s\' %(self.name,food))
Chinese.eat=eat_food
print(Chinese.__dict__)
#{\'__module__\': \'__main__\', \'__init__\': <function Chinese.__init__ at 0x000001F14768AB70>, \'play_ball\': <function Chinese.play_ball at 0x000001F14768ABF8>, \'__dict__\': <attribute \'__dict__\' of \'Chinese\' objects>, \'__weakref__\': <attribute \'__weakref__\' of \'Chinese\' objects>, \'__doc__\': None, \'eat\': <function eat_food at 0x000001F1459C3E18>}
p1.eat(\'饭\')
#qf 正在吃饭
def test(self):
print(\'test\')
Chinese.play_ball=test
p1.play_ball()# Chinese.play_ball(p1)
#test
对象属性操作
class Chinese:
country=\'China\'
def __init__(self,name):
self.name=name
def play_ball(self,ball):
print(\'%s 正在打 %s\' %(self.name,ball))
p1=Chinese(\'qf\')
print(p1.__dict__)
print(p1.play_ball)
查看
# print(p1.name)
# print(p1.play_ball)
增加
# p1.age=18
# print(p1.__dict__)
# print(p1.age)
不要修改底层的属性字典
# p1.__dict__[\'sex\']=\'male\'
# print(p1.__dict__)
# print(p1.sex)
修改
# p1.age=19
# print(p1.__dict__)
# print(p1.age)
删除
# del p1.age
# print(p1.__dict__)
对象之间的交互
对战游戏小练习(王者荣耀)
定义铠爹类
class KaiDie:
camp=\'zhan_shi\'
def __init__(self,nickname,
aggressivity=100,
life_value=5000,
money=13888,
armor=30):
self.nickname=nickname
self.aggressivity=aggressivity
self.life_value=life_value
self.money=money
self.armor=armor
def attack(self,enemy):
damage_value=self.aggressivity-enemy.armor
enemy.life_value-=damage_value
定义王昭君类
class ZhaoJun:
camp=\'fa_shi\'
def __init__(self,nickname,
aggressivity=120,
life_value=5000,
money=13888,
armor=10):
self.nickname=nickname
self.aggressivity=aggressivity
self.life_value=life_value
self.money=money
self.armor=armor
def attack(self,enemy):
damage_value=self.aggressivity-enemy.armor
enemy.life_value-=damage_value
定义装备
class Hat:
def __init__(self,price=2300,aggrev=35,life_value=100):
self.price=price
self.aggrev=aggrev
self.life_value=life_value
def update(self,obj):
obj.money-=self.price #减钱
obj.aggressivity+=self.aggrev #加攻击
obj.life_value+=self.life_value #加生命值
def destroy(self,obj): #这是该装备的主动技能,毁灭,毁灭对方
obj.life_value-=1000 #假设毁灭的攻击力是1000
测试交互
z1=ZhaoJun(\'王昭君\')
k1=KaiDie(\'铠爹\')
h1=Hat()
print(z1.aggressivity,z1.life_value,z1.money) #z1的攻击力,生命值,护甲
if z1.money > h1.price:
z1.h1=h1
h1.update(z1)
print(z1.aggressivity,z1.life_value,z1.money) #z1的攻击力,生命值,护甲
print(k1.life_value)
z1.attack(k1) #普通攻击
print(k1.life_value)
z1.h1.destroy(k1) #用装备攻击
print(k1.life_value) #k1的生命值小于0就死了
练习
自己使用类定义一个对战类游戏,并且实现他们的交互