初识面向对象

156 阅读8分钟

面向对象前戏

​ 面向对象的理念如若用文字概念生搬硬套,不免有些晦涩难懂,所以我们采用代码编写人狗大战的方式来深入理解面向对象。

人狗大战

首先创造出人和狗

1.'''推导步骤1:用字典模拟人和狗'''
person1 = {
    'name': 'jason',
    'p_type': '猛男',
    'attack_val': 10,
    'life_val': 1000
}
person2 = {
    'name': 'kevin',
    'p_type': '闷骚男',
    'attack_val': 20,
    'life_val': 800
}
dog1 = {
    'name': '小黑',
    'd_type': '泰迪',
    'attack_val': 40,
    'life_val': 600
}
dog2 = {
    'name': '小黄',
    'd_type': '秋田',
    'attack_val': 30,
    'life_val': 600
}
# 由于我们定义人和狗的字典基本不变,而这些字典我们每次新定义一个人或狗都要重新写一份代码,(同一份代码,多个地方多次使用)非常的麻烦,所以我们考虑用函数来封装创建人和狗的代码

2.'''推导步骤2:用函数来封装创建人和狗的代码'''
def get_person(name, p_type, attack_val, life_val):
    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):
    dog_obj = {
        'name': name,
        'd_type': d_type,
        'attack_val': attack_val,
        'life_val': life_val
    }
    return dog_obj
这样,我们每次调用get_person,都能创建出一个人或狗
person1 = get_person('jason', '猛男', 10, 1000)
dog1 = get_dog('小黑', '秋田', 20, 300)

3.'''推导步骤3:给予人和狗攻击对方的能力,其实就是定义两个攻击的函数供两者调用'''
def person_attack(person, dog):
    # 提前展示被打的狗的状态
    print(f'即将被打的狗:{dog["name"]}, 当前血量:{dog["life_val"]}')
    # 最简单暴力的攻击手段:狗的血量 - 人的攻击力
    dog['life_val'] -= person['attack_val']
    # 展示被打后的状态
    print(f'狗{dog["name"]}被人{person["name"]}打了一拳,掉血{person["attack_val"]},当前血量:{dog["life_val"]}')
    
def dog_attack(dog, person):
    # 提前展示被打的人的状态
    print(f'即将被打的人:{person["name"]}, 当前血量:{person["life_val"]}')
    # 最简单暴力的攻击手段:人的血量 - 狗的攻击力
    person['life_val'] -= dog['attack_val']
    # 展示被打后的状态
    print(f'人{person["name"]}被狗{dog["name"]}咬了一口,掉血{dog["attack_val"]},当前血量:{person["life_val"]}')

# 开始互相攻击  

person_attack(person1, dog1)
# 即将被打的狗:小黑, 当前血量:300
# 狗小黑被人jason打了一拳,掉血10,当前血量:290
dog_attack(dog1, person1)
# 即将被咬的人:jason, 当前血量:1000
# 人jason被狗小黑咬了一口,掉血20,当前血量:980

至此,最初版本的人狗大战已经告一段落,接下来是优化版本。

代码优化

问题分析:
    人和狗的攻击函数可以被任意调用:狗能调用人的攻击函数,人能使用狗的攻击函数。
    person_attack(dog1, person1)
# 即将被打的狗:jason, 当前血量:980
# 狗jason被人小黑打了一拳,掉血20,当前血量:960
可以看到人和狗的攻击乱了套,那么我们能不能让人只能调用人攻击的函数,狗只能调用狗攻击的函数呢?

'''推导步骤4:人跟人的攻击函数绑定,狗跟狗的攻击函数绑定。'''
我们定义的函数默认情况下都是可以被任意调用的 但是现在我们想实现定义的函数只有特定的东西才可以调用:把攻击函数定义在 get_person局部名称空间里
    
def get_person(name, p_type, attack_val, life_val):
    def person_attack(person, dog):
        # 提前展示被打的狗的状态
        print(f'即将被打的狗:{dog["name"]}, 当前血量:{dog["life_val"]}')
        # 最简单暴力的攻击手段:狗的血量 - 人的攻击力
        dog['life_val'] -= person['attack_val']
        # 展示被打后的状态
        print(f'狗{dog["name"]}被人{person["name"]}打了一拳,掉血{person["attack_val"]},当前血量:{dog["life_val"]}')
    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):
    def dog_attack(dog, person):
        # 提前展示被打的人的状态
        print(f'即将被打的人:{person["name"]}, 当前血量:{person["life_val"]}')
        # 最简单暴力的攻击手段:人的血量 - 狗的攻击力
        person['life_val'] -= dog['attack_val']
        # 展示被打后的状态
        print(f'人{person["name"]}被狗{dog["name"]}咬了一口,掉血{dog["attack_val"]},当前血量:{person["life_val"]}')
    dog_obj = {
        'name': name,
        'd_type': d_type,
        'attack_val': attack_val,
        'life_val': life_val
    }
    return dog_obj   

# 如此我们再想用狗去调用人的攻击函数就万万不能了,经过以上步骤我们就将人的数据(人的信息字典)和人的攻击函数(功能)绑定到了一起,其实 
'将功能和数据绑定到一起的操作就是面向对象编程的一种思想体现。'

总结

将人的数据跟人的功能绑定到一起  只有人可以调用人的功能.
将狗的数据跟狗的功能绑定到一起  只有狗可以调用狗的功能.
我们将数据与功能绑定到一起的操作起名为:'面向对象编程' 。
本质:将特定的数据与特定的功能绑定到一起 将来只能彼此相互使用

编程思想的转变

1.面向过程编程
    截止昨天 我们所编写的代码都是面向过程编程
    过程其实就是流程 面向过程编程其实就是在执行一系列的流程
    eg:	注册功能   登录功能   冻结账户  ...
    就是按照指定的步骤依次执行 最终就可以得到想要的结果
2.面向对象编程
    核心就是'对象'二字
    对象就是一个容器,里边存放的是这个对象的数据和功能。
    例如:游戏里,我们创造出后羿这个英雄,我们只负责让这个英雄拥有它独有的数据和技能,至于它的战绩如何,我们程序员不用管。
'''
    面向过程编程相当于让你给出一个问题的具体解决方案
    面向对象编程相当于让你创造出一些事物之后不用你管
'''   
上述两种编程思想没有优劣之分 仅仅是使用场景不同 甚至很多时候是两者混合使用

对象与类的概念

对象:数据与功能的结合体
类:多个对象相同的数据个功能的结合体

或者说:'类是对象的抽象,对象是类的实体。'

类主要用于记录多个对象相同的数据和功能
对象则用于记录多个对象不同的数据和功能
在面向对象编程中,类仅仅用于节省代码,对象才是核心。

1.jpg

对象与类的创建

在现实生活中理论是应该先有一个个的个体(对象)再有一个个的群体(类)
在编程世界中必须要先有类才能产生对象

创建类的语法
class 类名:
	对象公共的数据
	对象公共的方法
1.	class 是定义类的关键字
2.      类名与变量名的命名规则一致,并且推荐首字母大写(方便与函数名区分)
3.	与函数不同的是:类体代码会在类的定义阶段就执行

class Student:
    school: '清华大学'
    def run(self):
        print('student run')
1.查看类的名称空间的方法
print(Student.__dict__)  # 使用该方法查看名称空间,得到的是一个字典
2.可以使用字典的方式取值
print(Student.__dict__.get('school'))  # 清华大学
print(Student.__dict__.get('run'))  # <function Student.run at 0x000002102DE2A310>

'在面向对象中,想要获取名称空间的名字可以使用句点符的形式,'由此上述代码可简化为:
print(Student.school)
print(Student.run)
'''类实例化产生对象:类名加括号'''此时的类就像一个制造机,我们加括号'调用'一次,就会产生一个student对象
stu1 = Student()
stu2 = Student()
1.此时的两个学生对象中由于没有各自独有的数据,所以调用__dict__后返回的是空字典
print(stu1.__dict__, stu2.__dict__)  # {} {}
2.虽然是空字典,但是他们依然可以访问创造他们的类中的属性
print(stu1.school)  # 清华大学
print(stu2.school)  # 清华大学
3.两个student对象虽然长得一样,但他们的内存地址不一样
print(stu1)  # <__main__.Student object at 0x000001D923B04A60>
print(stu2)  # <__main__.Student object at 0x0000025E8A48F130>
4.修改类中的属性,对象中的该属性也会跟着修改
Student.school = '北京大学'  # 修改school键对应的值
print(stu1.school)  # 北京大学
print(stu2.school)  # 北京大学
'我们习惯将类或者对象句点符后面的名字称为属性名或者方法名'

对象独有的数据

class Student:
	# 学生对象公共的方法
    def run(self):  
        print('student run')
	# 学生对象公共的数据
    school = '清华大学' 
    
stu1 = Student()    
1.'''推导思路1: 直接利用__dict__方法朝字典添加键值对'''  
stu1.__dict__['name'] = 'jason'  # 等价于 stu1.name = 'jason'
stu1.__dict__['age'] = 18  # 等价于 obj1.age = 18
stu1.__dict__['gender'] = 'male' 
这样一个一个添加属实费时间,若果有多个stu都要添加就太麻烦了

2.'''推导思路2: 将添加独有数据的代码封装成函数'''
def init(obj,name,age,gender):
    obj.__dict__['name'] = name
    obj.__dict__['age'] = age
    obj.__dict__['gender'] = gender
stu1 = Student()
print(stu1.__dict__)

3.'''推导思路3: init函数是专用给学生对象创建独有的数据 其他对象不能调用>>>:面向对象思想   将数据和功能整合到一起
将函数封装到学生类中 这样只有学生类产生的对象才有资格访问
'''
class Student:
    # 1.先产生一个空对象
    # 2.自动调用类里边的__init__方法,将产生的空对象当做第一个参数传入
    # 3.将产生的对象返回出去

    def __init__(self, name, age, gender):
        self.name = name  # 相当于 self.__dict__['name'] = name
        self.age = age
        self.gender = gender
    def run(self):  # 功能
        print('student run')
    school = '清华大学'  # 数据
    
stu1 = Student('jason', 18, 'male')
print(stu1)  # <__main__.Student object at 0x00000159E9598FD0>
print(stu1.__dict__)  # {'name': 'jason', 'age': 18, 'gender': 'male'}