第十三章 面向对象
1 面向对象概念
1.1 面向过程编程
面向过程的程序设计:核心是过程二字,过程指的是解决问题的步骤,即先干什么再干什么......面向过程的设计就好比精心设计好一条流水线,是一种机械式的思维方式。
优点是:复杂度的问题流程化,进而简单化(一个复杂的问题,分成一个个小的步骤去实现,实现小的步骤将会非常简单
缺点是:一套流水线或者流程就是用来解决一个问题,生产汽水的流水线无法生产汽车,即便是能,也得是大改,改一个组件,牵一发而动全身。
应用场景:一旦完成基本很少改变的场景,著名的例子有Linux內核,git,以及Apache HTTP Server等。
1.2 面向对象编程
面向对象的程序设计:核心是对象二字,要理解对象为何物,必须把自己当成上帝,上帝眼里世间存在的万物皆为对象,不存在的也可以创造出来。对象是特征与技能的结合体,基于面向对象设计程序就好比在创造一个世界,你就是这个世界的上帝,存在的皆为对象,不存在的也可以创造出来,与面向过程机械式的思维方式形成鲜明对比,面向对象更加注重对现实世界的模拟,是一种“上帝式”的思维方式。
面向对象的程序设计好比如来设计西游记,如来要解决的问题是把经书传给东土大唐,如来想了想解决这个问题需要四个人:唐僧,沙和尚,猪八戒,孙悟空,每个人都有各自的特征和技能(这就是对象的概念,特征和技能分别对应对象的数据属性和方法属性,然而这并不好玩,于是如来又安排了一群妖魔鬼怪,为了防止师徒四人在取经路上被搞死,又安排了一群神仙保驾护航,这些都是对象。然后取经开始,师徒四人与妖魔鬼怪神仙交互着直到最后取得真经。如来根本不会管师徒四人按照什么流程去取
优点是:解决了程序的扩展性。对某一个对象单独修改,会立刻反映到整个体系中,如对游戏中一个人物参数的特征和技能修改都很容易。
缺点:
编程的复杂度远高于面向过程,不了解面向对象而立即上手基于它设计程序,极容易出现过度设计的问题。一些扩展性要求低的场景使用面向对象会徒增编程难度,比如管理linux系统的shell脚本就不适合用面向对象去设计,面向过程反而更加适合。
无法向面向过程的程序设计流水线式的可以很精准的预测问题的处理流程与结果,面向对象的程序一旦开始就由对象之间的交互解决问题,即便是上帝也无法准确地预测最终结果。于是我们经常看到对战类游戏,新增一个游戏人物,在对战的过程中极容易出现阴霸的技能,一刀砍死3个人,这种情况是无法准确预知的,只有对象之间交互才能准确地知道最终的结果。
应用场景:需求经常变化的软件,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方
注意:面向对象的程序设计并不是全部。对于一个软件质量来说,面向对象的程序设计只是用来解决扩展性。
2 类与对象
2.1 类于对象的关系
一切皆对象
类是对对象的描述
类是对象的抽象,对象是类的实现
2.2 类的概念
类即类别、种类,是面向对象设计最重要的概念,对象是特征与技能的结合体,而类则是一系列对象相似的特征与技能的结合体
那么问题来了,先有的一个个具体存在的对象(比如一个具体存在的人),还是先有的人类这个概念,这个问题需要分两种情况去看
在现实世界中:先有对象,再有类
世界上肯定是先出现各种各样的实际存在的物体,然后随着人类文明的发展,人类站在不同的角度总结出了不同的种类,如人类、动物类、植物类等概念 也就说,对象是具体的存在,而类仅仅只是一个概念,并不真实存在
在程序中:务必保证先定义类,后产生对象
这与函数的使用是类似的,先定义函数,后调用函数,类也是一样的,在程序中需要先定义类,后调用类 不一样的是,调用函数会执行函数体代码返回的是函数体执行的结果,而调用类会产生对象,返回的是对象
2.3 定义类
在做的各位都是学生,一个学生是一个对象,
#在现实世界中: 从现有的对象 去抽象类
对象1:李二蛋
特征:
学校=交大
姓名=李二蛋
性别=男
年龄=18
技能:
学习
吃饭
睡觉
对象2:王翠花
特征:
学校=交大
姓名=王翠花
性别=女
年龄=38
技能:
学习
吃饭
睡觉
对象3:牛大炮
特征:
学校=交大
姓名=牛大炮
性别=男
年龄=78
技能:
学习
吃饭
睡觉
现实中的学生类
相似的特征:
学校=交大
相似的技能:
学习
吃饭
睡觉
在现实世界中:先有对象,再有类
#在程序中,务必保证:先定义(类),后使用(产生对象)
"""
1. 在程序中特征用变量标识,技能用函数标识
2. 因而类中最常见的无非是:变量和函数的定义
3. 类中的变量称之为属性, 类中的函数称之为方法
4. 注意: 方法本质上也是属性
"""
class Student:
school='sjtu'
def learn(self):
print('正在学习...')
def eat(self):
print('正在吃...')
def sleep(self):
print('正在睡...')
注意:
类中可以有任意python代码,这些代码在类定义阶段便会执行
因而会产生新的名称空间,用来存放类的变量名与函数名,可以通过
Student.__dict__
查看对于经典类来说我们可以通过该字典操作类名称空间的名字(新式类有限制),但python为我们提供专门的.语法
点是访问属性的语法,类中定义的名字,都是类的属性
2.4 直接访问类中属性
"""
. 是专门用来访问属性的运算符,本质操作的就是__dict__
"""
Student.school #等于经典类的操作 Student.__dict__['school']
Student.school='交大' #等于经典类的操作 Student.__dict__['school']='交大'
Student.x=1 #等于经典类的操作 Student.__dict__['x'] = 1
del Student.x #等于经典类的操作 Student.__dict__.pop('x')
2.5 对象的实例化
#调用类,或称为实例化,得到对象
s1 = Student()
s2 = Student()
s3 = Student()
# 根据 Student类 创建了三个对象
# 内存中开辟了三个空间 存放三个对象
# 类中定义的属性和方法 会映射到对象, 对象可以访问属性 调用方法
s1.school
s1.eat()
s1.learn()
s1.sleep()
# 每个对象都是不同的实体
s1.school = 'fudan'
print(s2.school) # sjtu
对象访问属性和类访问属性的区别
通过类对属性进行修改,会影响该类所有的实例
对象修改某个属性,只会影响这一个实例
2.6 self
我们注意到,类里面的每个方法,都自动接收一个参数self
, self
表示本对象,可以在类的内部访问属性、调用方法
注释 self
表示的是实例对象,不是类
class Student:
school='sjtu'
def learn(self):
print('正在学习...')
def eat(self):
print('正在吃...')
def sleep(self):
print('正在睡...')
def get_info(self):
print(self.school)
school.learn()
# self 不是关键字,换做其他的同样可以 类似于一个形参
class Student:
school='sjtu'
def learn(self):
print('正在学习...')
def eat(this):
print('正在吃...')
def sleep(self):
print('正在睡...')
def get_info(this):
print(this.school)
this.learn()
# 如果 方法需要参数跟在self 后面即可
class Student:
school='sjtu'
def learn(self):
print('正在学习...')
def eat(this):
print('正在吃...')
def sleep(self):
print('正在睡...')
def set_school(self, school):
self.school = school
s = Student()
s.set_school('fudan')
print(s.school)
2.7 __init__()
上面的三个对象,具有相同的属性。而前面我们看到现实生活中的学生对象是有不同的属性的,如姓名
姓名和年龄,定义不同的属性就用到了 __init__()
#注意:该方法是在对象产生之后才会执行,只用来为对象进行初始化操作,可以有任意代码,但一定不能有返回值
class Student:
school='sjtu'
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex=sex
def learn(self):
print('正在学习...')
def eat(this):
print('正在吃...')
def sleep(self):
print('正在睡...')
# 实例化对象
s1=Student('张三丰','男',18) #先调用类产生空对象s1,然后调用Student.__init__(s1,'李坦克','男',18)
s2=Student('李四光','女',38)
s3=Student('张五常','男',78)
s2.__dict__ # {'name': '王大炮', 'age': '女', 'sex': 38}
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__()
方法中一定不要有返回值
2.8 类和对象内置的特殊属性
类名.__name__ # 类的名字(字符串)
类名.__doc__ # 类的文档字符串
类名.__base__ # 类的第一个父类(在讲继承时会讲)
类名.__bases__ # 类所有父类构成的元组(在讲继承时会讲)
类名.__dict__ # 类的字典属性
类名.__module__ # 类定义所在的模块
类名.__class__ # 实例对应的类(仅新式类中)
对象.__doc__
对象.__dict__
对象.__class__
对象.__module__
对象.__dir__() # 返回列表,列表内是对象所有的属性