面向对象编程基础
面向对象前
问题
- 在未学习面向对象前,如果我们要解决创建对象的问题
- 如何完成
人狗大战
这个简单的小游戏
解决
以下我们会一步一步慢慢推导出如何用面向对象的思维去解决这个需求
首先需要创造出人和狗
- 推导步骤1: 直接手写字典模拟出人和狗
# 人
person = {
'name':'jason',
'p_type':'勇士',
'attack_val':300,
'life_val':2000
}
# 狗
dog = {
'name':'二胖',
'p_type':'金毛',
'attack_val':50,
'life_val':800
}
- 推导步骤2:由于定义人和狗的字典基本不变,但是在很多地方又需要反复使用,所以封装成函数
def get_person(name,p_type,attack_val,life_val):
"""
专门用于产生用户的字典(创造人)
:param name:
:param p_type:
:param attack_val:
:param life_val:
:return:
"""
person_obj = {
'name':name,
'p_type':p_type,
'attack_val':attack_val,
'life_val':life_val
}
return person_obj
def get_dog(name,d_type,attack_val,life_val):
"""
专门用于产生狗的字典(狗)
:param name:
:param p_type:
:param attack_val:
:param life_val:
:return:
"""
dog_obj = {
'name':name,
'd_type':d_type,
'attack_val':attack_val,
'life_val':life_val
}
return dog_obj
# 创造出一个人
person1 = get_person('jason','勇士',300,2000)
# 创造出一个人
dog1 = get_dog('二胖','金毛',50,300)
- 推导步骤3: 让人和狗具备攻击的能力 本质其实就是定义两个函数供人和狗调用
def person_attack(person_obj,dog_obj):
"""
专门提供给人调用 攻击狗
:param person_obj:
:param dog_obj:
"""
print('即将被攻击的狗:%s 当前血量:%s' % (dog_obj.get('name'), 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.get('life_val')))
def dog_attack(dog_obj, person_obj):
"""
专用提供给狗调用 攻击人
:param dog_obj: 传狗数据(字典)
:param person_obj: 传人数据(字典)
"""
print('即将被攻击的人:%s 当前血量:%s' % (person_obj.get('name'), 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.get('life_val')))
p = get_person('jason','勇士',300,3000)
d = get_dog('二胖','金毛',50,300)
person_attack(p,d)
dog_attack(d,p)
"""
输出结果:
即将被攻击的狗:二胖 当前血量:300
人:jason 锤了 狗:二胖 狗掉血:300 剩余血量:0
即将被攻击的人:jason 当前血量:3000
狗:二胖 咬了 人:jason 人掉血:50 剩余血量:2950
"""
- 由步骤3的代码我们可以发现,如果当狗调用人的方法,人调用狗的方法,那岂不是乱了套了?
p = get_person('jason','勇士',300,3000)
d = get_dog('二胖','金毛',50,300)
person_attack(d,p)
dog_attack(p,d)
"""
输出结果:(原本jason是人,二胖是狗,结果狗调用了人的方法,人调用的狗的方法)
即将被攻击的狗:jason 当前血量:3000
人:二胖 锤了 狗:jason 狗掉血:50 剩余血量:2950
即将被攻击的人:二胖 当前血量:300
狗:jason 咬了 人:二胖 人掉血:300 剩余血量:0
"""
- 将人和人攻击狗的函数绑定 狗和狗攻击人的函数绑定
这样产生人的时候,也会产生人攻击狗的函数,只有人可以调用产生狗对象时也会产生狗攻击人的函数,只有狗可以调用。
def get_person(name, p_type, attack_val, life_val):
"""
专用用于产生用户字典(创造人)
:param name: 姓名
:param d_type: 类型
:param attack_val:攻击力
:param life_val: 生命值
:return: 人的字典(人)
"""
def person_attack(person_obj, dog_obj):
"""
专用提供给人调用 攻击狗
:param person_obj: 传人数据(字典)
:param dog_obj: 传狗数据(字典)
"""
print('即将被攻击的狗:%s 当前血量:%s' % (dog_obj.get('name'), 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.get('life_val')))
person_obj = {
'name': name,
'p_type': p_type,
'attack_val': attack_val,
'life_val': life_val,
'person_attack': person_attack
}
return person_obj
def get_dog(name, d_type, attack_val, life_val):
"""
专门用于产生狗字典(狗)
:param name: 狗的名字
:param d_type: 狗的类型
:param attack_val: 狗的攻击力
:param life_val: 狗的生命值
:return: 狗的字典(狗)
"""
def dog_attack(dog_obj, person_obj):
"""
专用提供给狗调用 攻击人
:param dog_obj: 传狗数据(字典)
:param person_obj: 传人数据(字典)
"""
print('即将被攻击的人:%s 当前血量:%s' % (person_obj.get('name'), 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.get('life_val')))
dog_obj = {
'name': name,
'd_type': d_type,
'attack_val': attack_val,
'life_val': life_val,
'dog_attack': dog_attack
}
return dog_obj
d1 = get_dog('二胖', '金毛', 50, 300)
p1 = get_person('jason', '勇士', 300, 3000)
p1.get('person_attack')(p1, d1)
"""
输出结果:
即将被攻击的狗:二胖 当前血量:300
人:jason 锤了 狗:二胖 狗掉血:300 剩余血量:0
"""
总结
- 将人的数据跟人的功能绑定到一起,只有人可以调用人的功能
- 将狗的数据跟狗的功能绑定到一起,只有狗可以调用狗的功能
我们将数据与功能绑定到一起的操作起名为:面向对象编程
本质:将特定的数据与特定的功能绑定到一起,将来只能彼此相互使用
编程思想
面向过程编程
在此之前,我们所编写的代码都是面向过程编程
过程其实就是流程,面向过程编程其实就是在执行一系列的流程
eg:注册功能 登录功能 冻结账户
按照指定的步骤依次执行,最终就可以得到想要的结果
如下图:我们的操作系统就是面向过程编程的产物
面向对象编程
面向对象的核心就是对象二字,对象其实就是一个容器,里面将对象的数据和功能绑定到了一起。如游戏里面的人物一样,只负责创造出该人物以及该人物具备的功能,至于后续战绩如何无人知晓。
如:如果某位老板要开设公司,核心就是雇佣人(对象)做业务,他可以不管员工怎么做,只要负责聘请人来公司就行了,这就是面向对象思想
区别
面向过程编程相当于让你给出一个问题的具体解决方案,然后一步步去解决。
面向对象编程相当于让你创造出一些对象之后不用你管,然后通过这个对象去完成特定事情
上述两种编程思想没有优劣之分,仅仅是使用场景不同。甚至很多时候是两者混合使用
类
对象与类的概念
对象:数据与功能的结合体
类:多个对象相同的数据和功能的结合体
类主要用于记录多个对象相同的数据和功能
对象用于记录多个不同的数据和功能
注意:在面向对象编程中,类仅仅是用于节省代码,对象才是核心
对象与类的创建
- 在现实生活中理论是应该先有一个个的个体(对象),再有一个个的群体(类),而在编程的世界中必须要先有类才能产生对象
- 面向对象编程的本质是将数据和功能绑定到一起,但是为了突出面向对象编程的形式,python特地开发了一套语法专门用于面向对象编程的操作
class Student:
# 学生对象公共的数据
# 学生对象公共的方法
class
是定义类的关键字Student
是类的名字(类名的命名跟变量名一致,并且推荐首字母大写,为了更好的区分)- 类体代码(公共的数据/公共的方法)
类体代码可与函数不一样,它是在定义的时候就会执行,函数是被调用才会执行
- 类的实例化
"""
查看类中名称空间
"""
class Student:
school = '清华大学'
def choice_course(self):
return '正在选课'
# 查看Student类的名称空间
print(Student.__dict__)
# 查看Student类中school的值
print(Student.school) # 清华大学
# 查看Student类中choice_course函数
print(Student.__dict__.get('choice_course'))
'''在面向对象编程中 想要获取名称空间中的名字 可以采用句点符'''
print(Student.school) # 清华大学
print(Student.choice_course) # <function Student.choice_course at 0x10503b3a0>
"""类实例化产生对象>>>:类名加括号"""
st = Student()
print(st.school) # 清华大学
print(st) # <__main__.Student object at 0x1081e3af0>
print(st.__dict__) # {}
print(st.choice_course()) # 正在选课
Student.school = '北京大学'
print(st.school) # 北京大学
"""
我们习惯的将类或者对象句点符后面的东西成为属性吗或者方法名
"""
对象独有的数据
# 学生类
class Student:
# 学生对象公共的数据
school = '清华大学'
# 学生对象公共的方法
def choice_course(self):
print('正在选课')
'''推导思路1: 直接利用__dict__方法朝字典添加键值对'''
obj1 = Student()
obj1.__dict__['name'] = 'jason' # 等价于 obj1.name = 'jason'
obj1.__dict__['age'] = 18 # 等价于 obj1.age = 18
obj1.__dict__['gender'] = 'male' # ...
print(obj1.name)
print(obj1.age)
print(obj1.gender)
print(obj1.school)
obj2 = Student()
obj2.__dict__['name'] = 'kevin'
obj2.__dict__['age'] = 28
obj2.__dict__['gender'] = 'female'
print(obj2.name)
print(obj2.age)
print(obj2.gender)
print(obj2.school)
'''推导思路2: 将添加独有数据的代码封装成函数'''
def init(obj,name,age,gender):
obj.__dict__['name'] = name
obj.__dict__['age'] = age
obj.__dict__['gender'] = gender
stu1 = Student()
stu2 = Student()
init(stu1,'jason',18,'male')
init(stu2, 'kevin',28,'female')
print(stu1.__dict__)
print(stu2.__dict__)
'''推导思路3: init函数是专用给学生对象创建独有的数据 其他对象不能调用>>>:面向对象思想 将数据和功能整合到一起
将函数封装到学生类中 这样只有学生类产生的对象才有资格访问
'''
class Student:
"""
1.先产生一个空对象
2.自动调用类里面的__init__方法 将产生的空对象当成第一个参数传入
3.将产生的对象返回出去
"""
def __init__(self, name, age, gender):
self.name = name # obj.__dict__['name'] = name
self.age = age # obj.__dict__['age'] = age
self.gender = gender # obj.__dict__['gender'] = gender
# 左右两边的名字虽然一样 但是意思不一样 左边的其实是字典的键 右边的其实是实参
# 学生对象公共的数据
school = '清华大学'
# 学生对象公共的方法
def choice_course(self):
print('正在选课')
stu1 = Student()
print(stu1.__dict__)
Student.init(stu1, 'jason', 18, 'male')
print(stu1.__dict__)
print(stu1.name)
stu1 = Student('jason', 18, 'male')
print(stu1)
stu2 = Student('kevin', 28, 'female')
print(stu2)