19. 面向对象编程

1. 面向对象编程引入

1.0 分析属性

做一款人狗大战的小游戏

 

人的属性:

  人的姓名

  人的性别

  人的年龄

  人的生命值

  人的攻击力

 

狗的属性:

  狗的名字

  狗的年龄

  狗的生命值

  狗的攻击力

  狗的品种

1.1 方式一:面条版  使用字典定义属性

# 定义人和狗的参数
dog1 = {
    'name': '小黑',
    'age': 3,
    'hp': 100,
    'power': 30,
    'breed': ''
}

dog2 = {
    'name': '旺财',
    'age': 10,
    'hp': 100,
    'power': 20,
    'breed': ''
}

person1 = {
    'name': 'kevin',
    'gender': 'male',
    'age': 32,
    'hp': 1000,
    'power': 40,
}

person2 = {
    'name': 'leo',
    'gender': 'male',
    'age': 37,
    'hp': 1000,
    'power': 30
}

print(f"{person2['name']}这个人打了狗{dog1['name']},狗掉了{person2['power']}hp,还剩{dog1['hp'] - person2['power']}hp")
# leo这个人打了狗小黑,狗掉了30hp,还剩70hp

 1.2 方式二:函数版  封装成函数,减少代码冗余

解耦合:将某部分拆成小的部分,又将小的部分合并在一起

将人的属性、狗的属性,人打狗、狗咬人都定义函数

def init_person(name, gender, age, hp, power):
    info1 = {"name": name, "gender": gender, "age": age, "hp": hp, "power": power}
    return info1

def init_dog(name, age, hp, power, breed):
    info2 = {"name": name, "age": age, "hp": hp, "power": power, "breed": breed}
    return info2

def dog_attack_person(dog, person):
    """
    狗咬人函数
    :param dog:
    :param person:
    :return:
    """
    person["hp"] -= dog["power"]  # 人剩余的hp等于人的hp减狗的攻击力
    print(f"狗{dog['name']}咬了人{person['name']},人{person['name']}掉了{dog['power']}hp,还剩{person['hp']}hp")

def person_attack_dog(person, dog):
    """
    人打狗函数
    :param person:
    :param dog:
    :return:
    """
    dog["hp"] -= person["power"]
    print(
        f"{person['name']}这个人打了狗{dog['name']},狗掉了{person['power']}hp,还剩{dog['hp']}hp")

person1 = init_person(name='kevin', gender='male', age=32, hp=1000, power=40)
person2 = init_person(name='leo', gender='male', age=37, hp=1000, power=30)

dog1 = init_dog(name='小黑', age=3, hp=1000, power=30, breed='')
dog2 = init_dog(name='旺财', age=4, hp=1000, power=20, breed='')

# 小黑咬了kevin
dog_attack_person(dog=dog1, person=person1)  # 狗小黑咬了人kevin,人kevin掉了30hp,还剩970hp

# 小黑咬了leo 
dog_attack_person(dog=dog1, person=person2)  # 狗小黑咬了人leo,人leo掉了30hp,还剩970hp

1.3 方式三:聚合版

只有人能调用人打狗函数,将人的属性、人打狗两个函数定义成一个函数

只有狗能调用狗咬人函数,将狗的属性,狗咬人两个函数定义成一个函数

将动作函数放在字典中

def init_person(name, gender, age, hp, power):
    def person_attack_dog(person, dog):
        dog["hp"] -= person["power"]
        print(f"{person['name']}这个人打了狗{dog['name']},狗掉了{person['power']}hp,还剩{dog['hp']}hp")

    info1 = {"name": name, "gender": gender, "age": age,
             "hp": hp, "power": power,
             'person_attack_dog': person_attack_dog}
    return info1

def init_dog(name, age, hp, power, breed):
    def dog_attack_person(dog, person):
        person["hp"] -= dog["power"]  # 人剩余的hp等于人的hp减狗的攻击力
        print(f"狗{dog['name']}咬了人{person['name']},人{person['name']}掉了{dog['power']}hp,还剩{person['hp']}hp")

    info2 = {"name": name, "age": age, "hp": hp,
             "power": power, "breed": breed,
             'dog_attack_person': dog_attack_person}
    return info2

person1 = init_person(name='kevin', gender='male', age=32, hp=1000, power=40)
person2 = init_person(name='leo', gender='male', age=37, hp=1000, power=30)

dog1 = init_dog(name='小黑', age=3, hp=1000, power=30, breed='')
dog2 = init_dog(name='旺财', age=4, hp=1000, power=20, breed='')

# 小黑咬了leo
dog1['dog_attack_person'](dog1, person2)  # 狗小黑咬了人leo,人leo掉了30hp,还剩970hp

# kevin打了小黑
person1['person_attack_dog'](person1, dog1)  # kevin这个人打了狗小黑,狗掉了40hp,还剩960hp

1.4 总结

以上操作就是将数据与功能进行绑定

将数据与功能整合到一起的思想就是面向对象编程的思想

2. 面向过程与面向对象的概念

2.1 面向过程

过程的含义是将程序流程化,过程是流水线,按步骤解决问题

2.2 面向对象

面向对象的核心在于对象两个字,对象的含义是一个整体

对象就是容器,用来盛放数据和功能

面向对象的优点:解决了程序的扩展性,对某一个对象单独修改,会立刻反映到整个体系中

3. 类与对象

3.1 类的概念

类就是类别、种类的意思,是面向对象分析和设计的基石

如果多个对象有相似的数据与功能,那么该多个对象就属于同一个类
 
类的优点:
同一类对象相同的数据与功能存放到类里,而无需每个对象都重复存一份,这样每个对象里只需存自己独有的数据即可,极大地节省了空间。
 
对象是用来存放数据与功能的容器,类则是用来存放同一类多个对象相同的数据与功能的容器

 

类和对象哪个先产生?

 对于程序来说先有类才能有对象,对于人类的逻辑来说先有对象才能抽象出某一个类

 3.2 定义类的语法

class 类名(参数):
    代码体
# 类名建议使用大驼峰体
# 参数的括号可以不写
# 类中可以有任意python代码,这些代码在类定义阶段便会执行
# 因为会产生新的名称空间,用来存放类的变量名与函数名,可以通过类名.__dict__查看
# 点是访问属性的语法,类中定义的名字,都是类的属性

类的定义与使用举例
class Student:
    school = 'California'  # 定义学生类的数据
    grade = 3

    def reading(self):  # 定义学生类的功能
        print('读书')

    def rest(self):
        print('休息')


# 1.定义类之后,如何产生对象
stu1 = Student()  # <__main__.Student object at 0x000001FEAD21A110>
print(stu1)

# 2.查看对象的内置方法和属性
# print(dir(stu1))
# ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'exercise', 'grade', 'reading', 'school']

# 3.查看对象的名称空间
print(stu1.__dict__)  # {}
stu1.__dict__.update({'school': 'MIT'})
print(stu1.__dict__)  # {'school': 'MIT'}

# 4.调用对象中的属性
print(stu1.school)  # MIT
print(stu1.grade)  # 3
stu1.reading()  # 读书
stu1.rest()  # 休息

3.3 类的实例化---产生对象

调用类的过程称为将类实例化
拿到的返回值就是程序中的对象,或称为一个实例

 

 

3.4 修改对象的属性

(1)通过类的名称空间修改对象的属性

 举例:初始化对象的独有属性

class Student:
    name = ''
    gender = ''
    grade = None
    school = 'California'  # 定义学生类的数据---数据属性

    def reading(self):  # 定义学生类的功能---功能属性
        print('读书')

    def rest(self):
        print('休息')

stu1 = Student()
print(stu1.__dict__)  # {}
stu1.__dict__.update({'name': 'clinton', 'gender': 'male', 'grade':1})
print(stu1.__dict__)  # {'name': 'clinton', 'gender': 'male', 'grade': 1}
print(stu1.school)  # California
print(stu1.grade)  # 1

(2)通过函数来操作类的名称空间修改对象的属性---封装成函数

方法一:

将生成的对象和属性值传递给函数,由函数来修改属性

从类中得到的是一个空的对象

class Student:
    name = ''
    gender = ''
    grade = None
    school = 'California'  # 定义学生类的数据---数据属性

    def reading(self):  # 定义学生类的功能---功能属性
        print('读书')

    def rest(self):
        print('休息')


def revise_obj(obj, name, gender, grade):
    obj.__dict__.update({'name': name, 'gender': gender, 'grade': grade})


stu1 = Student()  # 生成对象
revise_obj(stu1, name='clinton', gender='male', grade=1)  # 将对象和属性传递给函数进行修改属性,返回值可以是空的

stu2 = Student()
revise_obj(stu2, name='robert', gender='male', grade=2)

方法二:

将类传递给函数,在函数中先产生空对象,再在函数中修改属性值,将对象返回给调用函数的变量名

从类中得到的是一个具有自身属性的对象

class Student:
    name = ''
    gender = ''
    grade = None
    school = 'California'  # 定义学生类的数据---数据属性

    def reading(self):  # 定义学生类的功能---功能属性
        print('读书')

    def rest(self):
        print('休息')

def create_obj(cls, name, gender, grade):
    stu_obj = cls()
    stu_obj.__dict__.update({'name': name, 'gender': gender, 'grade': grade})
    return stu_obj

stu1 = create_obj(cls=Student, name='messi', gender='male', grade=1)  # 将类传递给函数,由类加括号产生对象然后修改属性值
print(stu1.__dict__)  # {'name': 'messi', 'gender': 'male', 'grade': 1}

(3)封装到类中的方法

 方法一:将以上方法一修改对象属性的函数,封装成类中的一个方法

由类产生一个空的对象,调用类中的方法给对象修改属性

class Student:
    name = ''
    gender = ''
    grade = None
    school = 'California'  # 定义学生类的数据---数据属性

    def reading(self):  # 定义学生类的功能---功能属性
        print('读书')

    def rest(self):
        print('休息')

    def revise_obj(self, name, gender, grade):  # 将修改属性函数放在类中
        print(id(self))  # 1601271512992
        self.__dict__.update({'name': name, 'gender': gender, 'grade': grade})

stu1 = Student()  # 生成对象
stu1.revise_obj(name='clinton', gender='male', grade=1)
print(stu1.__dict__)  # {'name': 'clinton', 'gender': 'male', 'grade': 1}

print(id(stu1))  # 1601271512992
# 对象stu1与类中self的内存地址一致,因此self就是当前对象

方法二:

方法一的基础上在修改属性函数里将self即对象返回出去,调用该函数时,就会得到一个对象

方法一与方法二的区别是修改属性函数有无返回值,调用函数能否直接得到对象

class Student:
    name = ''
    gender = ''
    grade = None
    school = 'California'  # 定义学生类的数据---数据属性

    def reading(self):  # 定义学生类的功能---功能属性
        print('读书')

    def rest(self):
        print('休息')

    def revise_obj(self, name, gender, grade):
        self.__dict__.update({'name': name, 'gender': gender, 'grade': grade})
        return self  # 有返回值,并且返回值是带有自身属性的对象

stu1 = Student().revise_obj(name='monica', gender='female', grade=2)
print(stu1.__dict__)  # {'name': 'monica', 'gender': 'female', 'grade': 2}

(4)总结

给对象修改属性有两大方法

第一种是按方法一通过类的名称空间赋值的方法修改属性

第二种是将修改属性的函数封装在类里面,在给类传值生成对象时,对象就拿到这些值有了自身的属性

3.5 类的init方法---构造对象方法

方法一:

与上面将修对象属性的函数封装到类中相比,区别是修改属性函数名换成了_ _ init _ _,并且无法从_ _ init _ _返回值

class Student:
    # 不再需要将个人的变量名放在这里
    school = 'California'  # 定义学生类的数据---数据属

    def reading(self):  # 定义学生类的功能---功能属性
        print('读书')

    def rest(self):
        print('休息')

    def __init__(self, name, gender, grade):
        self.__dict__.update({'name': name, 'gender': gender, 'grade': grade})
        # 无法从__init__返回值


stu1 = Student(name='avril', gender='female', grade=3)
print(stu1.__dict__)  # {'name': 'avril', 'gender': 'female', 'grade': 3}

# 对象.属性名取值   或对象.属性名=属性值的替换值
stu1.grade = 4
print(stu1.__dict__)  # {'name': 'avril', 'gender': 'female', 'grade': 4}

方法二:

与方法一相比,区别是方法一修改属性是在_ _init_ _中,给对象的属性字典更新一个自身信息的字典,实现修改对象属性

而方法二是通过   对象.属性名 赋值的方法,实现修改对象属性

class Student:

   # 不再需要将个人的变量名放在这里 school = 'California' # 定义学生类的数据---数据属性 def reading(self): # 定义学生类的功能---功能属性 print('读书') def rest(self): print('休息') def __init__(self, name, gender, grade): self.name = name self.gender = gender self.grade = grade stu1 = Student(name='lavigne', gender='female', grade=2) print(stu1.__dict__) # {'name': 'lavigne', 'gender': 'female', 'grade': 2}

3.6 类属性与对象属性

class Student:
    school = 'California University'  # 数据属性

    # 函数属性
    def __init__(self, name, gender, grade):
        self.name = name
        self.gender = gender
        self.grade = grade

    def lab(self):
        print(f'{self.name}正在做实验')

# 给类传入参数,实例化类得到一个对象
stu1 = Student(name='lavigne', gender='female', grade=2)
# 查看对象的属性字典
print(stu1.__dict__)  # {'name': 'lavigne', 'gender': 'female', 'grade': 2}
# 查看类的属性字典
print(Student.__dict__)
# {'__module__': '__main__', 'school': 'California University', '__init__': <function Student.__init__ at 0x000001F2208F3370>, 
'lab': <function Student.lab at 0x000001F220B61750>, '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__':
<attribute '__weakref__' of 'Student' objects>, '__doc__': None}

3.7 类的其它方法

print(dir(Person))  # 查看类的所有属性和方法
print(Person.__doc__)  # 查看类的注释
print(Person.__name__)  # 查看类的名字
print(Person.__module__)  # 查看类所在的模块名字
print(Person.__bases__)  # 查看当前类进程的所有父类
print(Person.__base__)  # 查看当前类继承的第一个父类
print(Person.__class__)  # 查看当前对应的类type产生了每一个基类  

 

posted @ 2024-08-13 22:48  hbutmeng  阅读(25)  评论(0)    收藏  举报