1 > 面向对象的概述
提到面向对象,自然会想到面向过程。面向过程编程的基本思想是:分析解决问题的步骤,使用函数实现每步相应的功能,按照步骤的先后顺序依次调用函数。前面的博客中所展示的程序都以面向过程的方式实现,面向过程只考虑如何解决当前问题,它着眼于问题本身。
面向对象编程着眼于角色以及角色之间的联系。使用面向对象编程思想解决问题时,开发人员首先会从问题之中提炼出问题涉及的角色,将不同角色各自的特征和关系进行封装,以角色为主体,为不同角度定义不同的属性和方法,以描述角色各自的属性与行为。
1.1 > 面向对象的基本概念
1.1.1 > 对象(object)
从一般意义上讲,对象是现实世界中可描述的事物,它可以是有形的也可以是无形的,从一本书到家图书馆, 从单个整数到繁杂的序列等都可以称为对象。 对象是构成世界的一个独立单位,它由数据(描述事物的属性)和作用于数据的操作(体现事物的行为)构成一个独立整体。从程序设计者的角度看,对象是一一个程序模块,从用户来看,对象为他们提供所希望的行为。对象既可以是具体的物理实体的事物,也可以是人为的概念,如一名员工、一家公司、辆汽车、一个故事等。
1.1.2 > 类(Class)
俗话说“物以类聚”,从具体的事物中把共同的特征抽取出来,形成一般的概念称为 “归类”。忽略事物的非本质特牲,关注与目标有关的本质特征,找出事物间的共性,以抽象的手法构造一个概念模型,就是定义一一个类。
在面向对象的方法中,类是具有相同属性和行为的-组对象的集合,它提供- 个抽象的描述, 其内部包括属性和方法两个主要部分。类就像- - 个模具,可以用来铸造一个个具体的铸件对象。
1.1.3 > 抽象(Abstract)
抽象是抽取特定实例的共同特征,形成概念的过程,例如苹果、香蕉、梨、葡萄等,抽取出它们共同特性就得出“水果”这一类,那么得出水果概念的过程,就是一个抽象的过程。抽象主要是为了使复杂度降低,它强调主要特征,忽略次要特征,以得到较简单的概念,从而让人们能控制其过程或以综合的角度来了解许多特定的事态。
1.1.4 > 封装(Encapsulation)
封装是面向对象程序设计最重要的特征之一一。 封装就是隐藏, 它将数据和数据处理过程封装成一个整体,以实现独立性很强的模块,避免了外界直接访问对象属性而造成耦合度过高及过度依赖,同时也阻止了外界对对象内部数据的修改而可能引发的不可预知错误。
封装是面向对象的核心思想,将对象的属性和行为封装起来,不需要让外界知道具体实现细节,这就是封装思想。例如,人们对计算机进行封装,用户只需要知道通过鼠标和键盘可以使用计算机,但无须知道计算机内部如何工作。
1.1.5 > 通过代码理解
下面我们将通过编写一个人狗大战的小游戏,来简单描述面向对象的基本知识,代码如下:
# 1.描述人和狗
'''方式一:使用字典一个个的描述'''
dog1 = {
'name': '小八'
'type': '田园犬'
'attack_val': 30
'life_val':200
}
dog2 = {
'name': '小九'
'type': '哈士奇'
'attack_val': 30
'life_val':200
}
person1 = {
'name': '龙傲天',
'type': '猛男',
'attack_val': 10,
'life_val': 1000
}
'''方式2:封装成函数 减少代码冗余'''
# 有关描述人的函数
def get_person(name, gender, age, t_type, attack_val, life_val):
data_dict = {
'name':name,
'gender': gender,
'age': age,
't_type': t_type,
'attack_val': attack_val,
'life_val': life_val
}
return data_dict
# 有关描述狗的函数
def get_dog(name, t_type, attack_val, life_val):
data_dict = {
'name': name,
't_type': t_type,
'attack_val': attack_val,
'life_val': life_val
}
return data_dict
# 将人和狗的数据分别传入不同的函数中
p1 = get_person('bob', 'male', 18, '猛男', 800, 1000)
p2 = get_person('kevin', 'female', 28, '淑女', 5, 100)
dog1 = get_dog('小黑', '地狱恶犬', 300, 500)
dog2 = get_dog('小白', '泰迪犬', 50, 200)
print(p1, p2, dog1, dog2) # 可以看各个人和狗的信息
最后就可以编写一个简单的人狗大战的游戏了
'''人打狗的伤害函数'''
def person_attack(person_obj, dog_obj):
"""
:param person_obj: 接收一个人
:param dog_obj: 接收一条狗
"""
print('当前狗的血量是:%s'%dog_obj.get('life_val'))
dog_obj['life_val'] -= person_obj.get('attack_val')
print("""人:%s 锤了狗:%s 一下 狗掉血:%s 剩余血量:%s"""%(person_obj.get('name'),dog_obj.get('name'),person_obj.get('attack_val'),dog_obj['life_val']))
'''狗对人的伤害函数'''
def dog_attack(dog, person_obj):
"""
:param dog_obj:接收狗的信息
:param person_obj :接收人的信息
"""
print('当前人的血量是:%s' % person_obj.get('life_val'))
person_obj['life_val'] -= dog_obj.get('attack_val')
print("""狗:%S 咬了%s,人掉血%s,剩余血量:%s""" % (dog_obj.get('name'), person_obj.get('name'),dog_obj.get('attack_val'),person_obj['life_val']))
# 狗咬人
dog_attack(dog2,p1)
# 人打狗
person_attack(p2, dog1)
# 打印
# 当前人的血量是:1000
# 狗:小白 咬了bob,人掉血50,剩余血量:950
# 当前狗的血量是:1000
# 人:bob 打了:小白,狗掉血800,剩余血量:-300
上述的代码不是很严谨,甚至可以让狗调用人的伤害,人也可以调用人的伤害,那么我们如何做到只有人能调用人的攻击伤害,只有狗才可以调用狗的攻击伤害,具体操作如下:
def get_person(name, gender, age, t_type, attack_val, life_val):
'''人打狗的伤害函数'''
def person_attack(person_obj, dog_obj):
"""
:param person_obj: 接收一个人
:param dog_obj: 接收一条狗
"""
print('当前狗的血量是:%s' % dog_obj.get('life_val'))
dog_obj['life_val'] -= person_obj.get('attack_val')
print("""人:%s 锤了狗:%s 一下 狗掉血:%s 剩余血量:%s""" % (
person_obj.get('name'), dog_obj.get('name'), person_obj.get('attack_val'), dog_obj['life_val']))
data_dict = {
'name': name,
'gender': gender,
'age': age,
't_type': t_type,
'attack_val': attack_val,
'life_val': life_val,
'person_attack': person_attack # 添加一个键值对
}
return data_dict
'''将狗咬人功能函数放到定义到狗的信息函数内'''
def get_dog(name, t_type, attack_val, life_val):
def dog_attack(dog_obj, person_obj):
"""
:param dog_obj: 接收一条狗
:param person_obj: 接收一个人
"""
print('当前人的血量是:%s' % person_obj.get('life_val'))
person_obj['life_val'] -= dog_obj.get('attack_val')
print("""狗:%s 咬了人:%s 一口 人掉血:%s 剩余血量:%s""" % (
dog_obj.get('name'), person_obj.get('name'), dog_obj.get('attack_val'), person_obj['life_val']))
data_dict = {
'name': name,
't_type': t_type,
'attack_val': attack_val,
'life_val': life_val,
'dog_attack': dog_attack
}
return data_dict
p1 = get_person('jason','male',18,'猛男',800, 1000)
p2 = get_person('kevin','female',28,'淑女',10,100)
dog1 = get_dog('小黑', '松狮犬', 300, 500)
dog2 = get_dog('小白', '泰迪犬', 50, 200)
'''这样就不会出现人还可以调用狗的攻击功能了'''
p1['person_attack'](p1,dog1)
dog1['dog_attack'](dog1,p2
)
上述操作其实就是将数据与功能进行绑定,不再是所有的数据都可以调用任意的功能了,将数据与功能整合到一起的操作其实就是面对对象编程的思想
2 > 编程思想
2.1 > 面向过程编程
将程序的执行流程化 即分步操作 分步的过程中解决问题,例如,编写注册、登录、结算购物车… 过程可以理解成是流水线 面向过程编程可以理解成是在创建一条流水线。
2.2 > 面向对象编程
这种方式的编程核心就是对象二字。
对象其实就是一个"容器" 将数据与功能整合到一起
只要是符合上述描述的事物都可以称之为是对象!!!
3 > 类与对象的概念
面向对象编程思想力求在程序中对事物的描述与该事物在现实中的形态保持一致。为此,面向对象的思想中提出了两个概念;类和对象。类是对多个对象共同特征的抽象描述,是对象的模板;对象用于描述现实中的个体,它是类的实例。
3.1 > 类的定义与访问
在程序中创建对象之前需要先定义类。类是对象的抽象,是一种自定义数据类型,它用于描述一组对象的共同特征和行为。类中可以定义数据成员和成员函数,数据成员用于描述对象的特征,成员函数用于描述对象行为,其中数据成员也被称为属性,成员函数也被称为方法。下面介绍如何定义类,以及如何访问类的成员。
类的定义格式如下:
class 类名: # 使用class 定义类
属性名 = 属性值 # 定义属性
def 方法名(self): # 定义方法
方法体
以上格式中的class是定义类的关键字,其后的类名是类的标识符,类名首字母一般为大写。类名后的冒号(:)比不可少,之后的属性和方法都是类的成员,其中属性类似与前面学过的变量,方法类似于前面学过的函数,但需要注意,方法中有一个指向对象的默认参数self。
下面定义一个Car类,和Student类 代码如下:
class Car:
wheels = 4 # 属性
def drive(self): # 方法
print('开车的方式')
def stop(self): # 方法
print('停车的方式')
以上代码定义了一个汽车类Car,该类包含一个描述车轮数量的属性 wheels、一个描述开车方式的方法、一个描述停车方式的方法。
class Student:
school = '清华大学'
def choose_course(self):
print('学生选课功能')
3.2 > 查看名称空间
查看名称空间需要用到 dict 内置方法。
print(Studen.__dict__) # 返回值是一个字典
print(Student.__dict__['school']) # 获取类中的属性
print(Student.__dict__['choose_course']) # 获取类中的方法
"""类获取数据和功能有一个简便的方式>>>:句点符"""
print(Student.school)
print(Student.choose_course)
3.3 > 对象的创建于使用
类定义完成后不能直接使用,这就好比画好了一张房屋设计图纸,此图纸只能帮助人们了解房屋的结构,但不能提供居住场所,为满足居住需求,需要根据房屋设计图纸搭建实际的房屋。同理,程序中的类需要实例化为对象才能实现其意义。
1 > 创建格式如下:
对象名 = 类名()
例如创建一个上述定义的Student类的对象 obj_student,代码如下:
obj_student = Student()
2 > 若想在程序中真正地使用对象,需要掌握访问对象成员的方式。对象成员分为属性和方法,它们的访问格式分别如下:
对象名.属性 # 访问对象属性
对象名.方法() # 访问对象方法
obj1 = Student() # 类名加括号就是在产生一个对象
obj2 = Student()
print(obj1.__dict__, obj2.__dict__) # {} {}
print(obj1.school) # 清华大学
print(obj2.school) # 清华大学
print(obj1.choose_course) # bound method
# 修改名字与值的对应关系
Student.school = '北京大学' # Student.__dict__['school'] = '北京大学'
print(obj1.school) # 北京大学
print(obj2.school) # 北京大学
4 > 对象的实例化
4.1 > 方式一:逐步给对象添加独有的数据
class Student:
# 学生类公共的数据
school = '清华大学'
# 学生类公共的功能
def choose_course(self):
print('学生选课功能')
obj1 = Student()
obj2 = Student()
print(obj1.__dict__,obj2.__dict__)
# {} {}
obj1.__dict__['name'] = 'bob'
obj1.__dict__['age'] = 18
obj1.__dict__['gender'] = 'male'
obj2.__dict__['name'] = 'kevin'
obj2.__dict__['age'] = 25
obj2.__dict__['gender'] = 'female'
print(obj1.__dict__,obj2.__dict__)
# {'name': 'bob', 'age': 18, 'gender': 'male'} {'name': 'kevin', 'age': 25, 'gender': 'female'}
4.2 > 方式二:将冗余的添加步骤封装成函数
def init(obj, name, age, gender):
obj.__dict__['name'] = name # obj.name = name
obj.__dict__['age'] = age # obj.age = age
obj.__dict__['gender'] = gender # obj.gender = gender
init(obj1,'rr', 52, 'male')
init(obj2,'cc', 26, 'female')
print(obj1.name)
print(obj2.name)
# rr
# cc
4.3 > 方式三:简单的封装成函数没有提现出面向对象整合的精髓>>>:将函数写到类中去
class Student:
# 学生类公共的数据
school = '清华大学'
# 学生类公共的功能
def set_info(obj, name, age, gender):
obj.__dict__['name'] = name
obj.__dict__['age'] = age
obj.__dict__['gender'] = gender
def choose_course(self):
print('学生选课功能')
obj1 = Student()
obj2 = Student()
Student.set_info(obj1, 'bob', 18, 'male')
Student.set_info(obj2, 'jason', 15, 'female')
print(obj1.name)
print(obj2.name)
# bob
# jason
4.4 > 方式四 :类中针对给对象创建独有数据的函数名 专门定义了一个固定的方法 init
class Student:
# 学生类公共的数据
school = '清华大学'
# 学生类公共的功能
def __init__(obj, name, age, gender):
obj.__dict__['name'] = name
obj.__dict__['age'] = age
obj.__dict__['gender'] = gender
def choose_course(self):
print('学生选课功能')
obj1 = Student('jason', 18, 'male')
obj2 = Student('kevin', 23, 'female')
print(obj1.__dict__)
print(obj2.__dict__)
'''
类中的__init__方法会在类产生对象的时候自动执行
类产生对象的具体步骤
1.先创建一个没有独有数据的空对象 {}
2.将空对象和括号内传入的数据一并交给__init__执行
__init__的第一个参数就是对象本身
__init__(obj, name, age, gender)
3.将创建好的对象自动返回
print('学生选课功能')
print('学生选课功能')
ps:针对括号内第一个形参self其实就是一个普通的变量名而已
只不过该变量名将来专门接收对象的 所以给它起了个固定的名字叫self
'''
5 > 绑定方法
在类中定义的函数默认都是绑定给对象使用的,即对象来调 会自动将对象当做第一个参数传入
5.1 > 类调用函数
类调用类中函数 有几个参数就需要传几个参数
class Student:
school = '清华大学'
# __init__方法不要自己去调用
def __init__(self, name, age):
self.name = name
self.age = age
def func(self):
print(self)
Student.func(123)
# 123
5.2 > 对象调用类中函数
会将当前调用的对象当做第一个参数自动传入
class Student:
school = '清华大学'
# __init__方法不要自己去调用
def __init__(self, name, age):
self.name = name
self.age = age
def func(self):
print('%s正在调用func方法'%self.name)
def index(self):
print('%s正在调用index方法'%self.name)
obj1 = Student('jason', 18)
obj2 = Student('kevin', 28)
obj1.func() # jason正在调用func方法
obj2.func() # kevin正在调用func方法