【Python基础篇】【17.面向对象】(附案例,源码)超详细的保姆级攻略


类与对象

类型(汉语词语),指包含由各特殊的事物或现象抽出来的共通点的抽象概念;

类(Class)是面向对象程序设计(OOP,Object-Oriented Programming)实现信息封装的基础。

"""
面向对象: 类与对象
    类 : 是指一类按照一定的规则划分出来的一个群体
    对象 : 类中一个东西

    类   按照灵长类动物 人 -----> 类
    	我 在座的各位同学 都是  一个个对象

    在一个类里面:  所有的对象(个体)都是有一定的属性和行为
    泽言   属性:名字 age
    行为   老师会打篮球 唱歌 .... 这些就是行为方法(就是自己封装的函数)
"""

类与对象的关系我们可以用一个案例类比,假设你要买一张飞机票,并且你需要填写一份订票表格。这份订票表格就相当于一个类,它有一些属性(如你的姓名、航班编号、起点和终点等信息),而你填写的表格就是对象。在填写表格时,你需要根据表格的属性来输入相关信息,就像在使用类时需要调用类的属性和方法一样。

类与对象的关系

类是一个模板,对象是根据这个模板创建的具体实例。如果将类比喻为蛋糕模具,那么对象就是蛋糕。一个类可以创建多个对象,它们之间是独立的,互相不影响

类和对象的定义

类是一种用户自定义的数据类型,它由数据和方法组成。数据表示属性,方法表示行为。一个类可以包含多个属性和方法。属性是类的成员变量,可以存储数据。方法是一组操作数据的代码,它们可以实现某些功能或者改变属性的值。

class 类名:
    类属性 =def __init__(self, 参数):
        self.属性1 = 参数1
        self.属性2 = 参数2
    def 方法1(self, 参数):
        # 方法代码
    def 方法2(self, 参数):
        # 方法代码

面向对象

面向对象编程 —— Object Oriented Programming 简写 OOP

对象到底是什么,我们可以从两次层次来理解。

(1) 对象是单个事物的抽象。

一本书、一辆汽车、一个人都可以是对象,一个数据库、一张网页、一个与远程服务器的连接也可以是对象。当实物被抽象成对象,实物之间的关系就变成了对象之间的关系,从而就可以模拟现实情况,针对对象进行编程。

例如,一个“人”对象可以表示具有姓名,年龄,身高、体重等属性,具有吃饭,睡觉,呼吸和跑步等行为。

换句话说,面向对象编程是将现实世界的事物在程序中建立模型的关系方法,如汽车以及公司和员工,学生和教师等。OOP将现实世界的实体建模为软件对象,一些与之相关的数据,可以执行某些功能。

(2) 对象是一个容器,封装了属性(property)和方法(method)。

属性是对象的状态,方法是对象的行为(完成某种任务)。比如,我们可以把动物抽象为animal对象,使用“属性”记录具体是那一种动物,使用“方法”表示动物的某种行为(奔跑、捕猎、休息等等)。

在实际开发中,对象是一个抽象的概念,可以将其简单理解为:数据集或功能集

提示:每个对象都是基于一个引用类型创建的,这些类型可以是系统内置的原生类型,也可以是开发人员自定义的类型。

什么是面向对象

面向对象不是新的东西,它只是过程式代码的一种高度封装,目的在于提高代码的开发效率和可维护性。

在这里插入图片描述

  • 面向对象编程(Object-Oriented Programming,OOP)是一种常用的编程思想,它强调万物皆对象,因此在编程时我们可以将现实世界中的事物抽象成程序中的对象,从而更好实现软件的设计与开发。与传统的基于函数的编程不同,面向对象编程注重于将数据与行为封装在一起,即对象既包含数据状态,还包含可调用的行为方法。
  • 面向对象编程的特点在于,它具有封装、继承和多态三大特性。封装意味着将对象的状态和行为进行封装,使其对外只暴露必要的接口,从而提高了安全性和可维护性;继承指的是某个对象可以继承另一个对象的特性,从而快速构建具有相似属性的对象;多态是指同一种行为在不同的对象上具有不同的表现形式,即在不同的情境下,同一个方法可以被不同的对象进行调用。
  • 在面向对象程序开发思想中,每一个对象都是功能中心,具有明确分工,可以完成接受信息、处理数据、发出信息等任务。因此,面向对象编程具有灵活、代码可复用、高度模块化等特点,容易维护和开发,比起由一系列函数或指令组成的传统的过程式编程(procedural programming),更适合多人合作的大型软件项目。

总之,面向对象编程是一种强大的编程方式,它具有高度封装性、灵活的继承性和强大的多态性,通过使用对象作为程序的基本处理单元,实现了数据和行为的有机结合,可以使程序更加高效、结构清晰,并方便管理和扩展。

面向对象与面向过程:

  • 面向过程就是亲力亲为,事无巨细,面面俱到,步步紧跟,有条不紊
  • 面向对象就是找一个对象,指挥得结果
  • 面向对象将执行者转变成指挥者
  • 面向对象不是面向过程的替代,而是面向过程的封装

面向对象的特性:

  • 封装性
  • 继承性
  • [多态性]抽象

面向对象的具体表现

在 Python 中,所有数据类型都可以视为对象,当然也可以自定义对象。
自定义的对象数据类型就是面向对象中的类( Class )的概念。

我们以一个例子来说明面向过程和面向对象在程序流程上的不同之处。

假设我们要处理学生的成绩表,为了表示一个学生的成绩,面向过程的程序可以用一个对象表示:

std1 = { 'name': '小明', 'score': 98 }
std2 = { 'name': '小红', 'score': 81 }

而处理学生成绩可以通过函数实现,比如打印学生的成绩:

def print_score(student):
    print('姓名:' + student['name'] + '  ' + '成绩:' + str(student['score']))

如果采用面向对象的程序设计思想,我们首选思考的不是程序的执行流程,
而是 Student 这种数据类型应该被视为一个对象,这个对象拥有 namescore 这两个属性(Property)。
如果要打印一个学生的成绩,首先必须创建出这个学生对应的对象,然后,给对象发一个 print_socre 消息,让对象自己把自己的数据打印出来。

抽象数据行为模板(Class):

class Student(object):
    def __init__(self, name, score):
        self.name = name
        self.score = score

    def print_score(self):
        print('姓名:' + self.name + '  ' + '成绩:' + str(self.score))

根据模板创建具体实例对象(Instance):

std1 = Student('小明', 98)
std2 = Student('小红', 81)

实例对象具有自己的具体行为(给对象发消息):

std1.print_socre() #  => 姓名:Michael  成绩:98
std2.print_socre() #  => 姓名:Bob  成绩 81

面向对象的设计思想是从自然界中来的,因为在自然界中,类(Class)和实例(Instance)的概念是很自然的。
Class 是一种抽象概念,比如我们定义的 Class——Student ,是指学生这个概念,
而实例(Instance)则是一个个具体的 Student ,比如, Michael 和 Bob 是两个具体的 Student 。

所以,面向对象的设计思想是:

  • 抽象出 Class(构造函数)
  • 根据 Class(构造函数) 创建 Instance
  • 指挥 Instance 得结果

面向对象的抽象程度又比函数要高,因为一个 Class 既包含数据,又包含操作数据的方法。

Python的类

Python中可用的原始数据结构(如数字,字符串和列表)旨在分别表示简单的事物,例如进行简单的数学运算,动物的名称和你喜欢的颜色。

如果你想代表更复杂的东西怎么办?

例如对于动物,我们可以创建一个Animal() 类来跟踪动物的属性,如名称和年龄。

重要的是要注意一个类只提供结构 ——它是应该如何定义某个东西的模板,但它实际上并不提供任何真实的内容。该 Animal() 可以指定姓名和年龄是必要的界定动物,但它实际上并不会说出什么特定动物的姓名或年龄。

类和对象的概念

对象 是面向对象编程的两个核心概念

是对一群具有相同 特征 或者 行为 的事物的一个统称,是抽象的。

  • 特征 被称为 属性
  • 行为 被称为 方法

类和对象的关系

  • 是模板,对象 是根据 这个模板创建出来的,应该先有类,再有对象
  • 只有一个,而 对象 可以有很多个
    • 不同的对象 之间 属性 可能会各不相同
    • 中定义了什么 属性和方法对象 中就有什么属性和方法

在这里插入图片描述

在这里插入图片描述

1.创建类和对象

首先,类是我们在面向对象编程中的基础,它是一种用来描述具有相同属性和方法的对象集合的蓝图。举个例子,我们可以创建一个名为 “人” 的类,这个类里面包含了姓名、年龄、性别等属性,以及 eat、sleep、work 等方法。然后我们可以实例化这个 “人” 类,创建很多具有不同属性的人的对象。

# 首先,我们来创建一个“人”类
class Person:
    # 把属性放在 __init__ 方法里面,注意第一个参数永远是 self,代表该类的实例
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender

    # 这里是一个日常生活中的行为,写成方法
    def eat(self):
        print("{} 在吃饭".format(self.name))

    # 再来一个睡觉
    def sleep(self):
        print("{} 正在睡觉".format(self.name))

    # 最后一个工作
    def work(self):
        print("{} 在工作".format(self.name))


# 现在我们创建两个人的对象
p1 = Person("小明", 18, "男")
p2 = Person("小红", 22, "女")

# 通过对象调用方法
p1.eat()  # 小明 在吃饭
p2.sleep()  # 小红 正在睡觉

'''
创建了一个名为 "Person" 的类,并定义了 "name"、 "age"、 "gender" 等属性,然后定义了 "eat"、 "sleep"、 "work" 等方法。通过实例化这个类,我们可以创建不同名字、不同年龄、不同性别的人,来调用这些方法。
'''

2.初始化方法和实例属性

初始化方法是在创建一个新的对象实例时,执行其中的代码,以初始化对象的属性和状态。Python 中的初始化方法通常为 init(),它是所有类中的可选方法。当您创建一个类的新实例时,Python 将自动调用 init() 方法,并将该实例作为第一个参数传递给它,并使用该实例来设置各种属性和状态。

初始化方法 __init__()

'''
__init__() 方法的重要性体现在两点。首先,初始化既是对象生命周期的开始,也是非常重要的一个步骤,每个对象都必须正确地执行了初始化才能够正常地工作。其次,__init()__ 方法的参数可以多种形式来完成赋值。

通过实现__init()__ 方法来初始化一个对象。每当创建一个对象,Python会先创建一个空对象,然后调用该对象的__init()__ 函数。这个方法提供了对象内部变量以及其他一些一次性过程的初始化操作
'''
# 定义人类
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

'''
定义了一个新的类 Person,并在其中定义了初始化方法 init()。此方法将 name 和 age 作为参数传递,并使用 self.name 和 self.age 对象属性分别赋值
'''
# 实例化对象
person1 = Person("小明", 20)
print("这个人的名字是:", person1.name)  # 这个人的名字是:小明
print("这个人的年龄是:", person1.age)  # 这个人的年龄是:20

初始化属性

一个对象是一系列功能的集合,包括了方法和属性。object 类的默认行为包括设置、获取和删除属性。可以通过修改这些默认行为来决定对象中哪些属性是可用的。

默认情况下,创建任何类内部的属性都将支持以下4种操作。

  • 创建新属性。
  • 为已有属性赋值。
  • 获取属性的值。
  • 删除属性。

我们可以使用如下简单的代码来对这些操作进行测试,创建一个简单的泛型类并将其实例化。

  • __init__ 方法内部使用 self.属性名 = 属性的初始值 就可以 定义属性
  • 定义属性之后,再使用 Car 类创建的对象,都会拥有该属性
'''
实例属性是方法内部定义的属性,它们与类的实例相关联,并且每个实例都有自己的一套实例属性。
'''
class Car:
    def __init__(self):
        self.color = "黑色"
        self.brand = "奥迪"
        self.year = 2023
 
    def describe(self):
        print("这辆车是一辆 %d 年的 %s %s ,颜色为 %s" % (self.year, self.brand, self.model, self.color))
 
car1 = Car()
# 获取属性
print(car1.color)
print(car1.brand)

# 外部新增属性
car1.model = "Q7"  # 为对象手动添加一个属性

car1.describe() # 这辆车是一辆 2023 年的 奥迪 Q7 ,颜色为 黑色

基类中的init方法

对象的生命周期主要包括了创建、初始化和销毁。后面章节会详细讨论对象的创建和销毁,本章专注于对象的初始化。

object作为所有类的基类,已经为__init__() 方法提供了默认实现,一般情况下不需要重写这个函数。如果没有对它进行重写,那么在创建对象时将不会产生其他变量的实例。在某些情况下,这种默认行为是可以接受的。

对于继承自 object 的子类,总可以对它的属性进行扩展。例如,对于下面这个类,实例化就不对函数(area )所需要的变量(widthlength )进行初始化。

class Person(object):

    # def __init__(self):
    #     pass

    def hello(self):
        print('我的名字是:{},已经:{}岁了'.format(self.name, self.age))

Person 类的 hello 函数在返回值时使用了两个属性,可并没有在任何地方对其赋值。在Python中,这种看似奇怪的调用尚未赋值属性的操作却是合法的。

下面这段代码演示如何使用刚定义的Person 类。

>>> tom = Person()
>>> tom.name = '汤姆'
>>> tom.age = '18'
>>> tom.hello()
我的名字是:汤姆,已经:18岁了

虽然这种延迟赋值的实现方式在Python中是合法的,但是却给调用者带来了潜在的困惑,因此要尽量避免这样的用法。

然而,这样的设计看似又提供了灵活性,意味着在__init__() 方法被调用时不必为所有的属性赋值。这看似是不错的选择,一个可选属性即可以看作是某子类中的成员,且无须对这个子类进行显式地定义就可以完成对原生机制的扩展。然而这种多态机制不但给程序带来了隐藏的不确定性,也会相应产生很多令人费解的if 语句。

因此,延迟初始化属性的设计在某种情形下可能会有用,可是这样也可能会导致非常糟糕的设计。

Zen of python poem 一书中曾提出过这样的建议:

“显式而非隐式”。

对于每个__init__() 方法,都应当显式地指定要初始化的变量。

糟糕的多态 在灵活性与糟糕之间有一个临界。 一旦发觉书写了这样的代码,我们就已经丧失了灵活性并开始了糟糕的设计。   if 'x' in self.__dict__: 或:   try:     self.x  except AttributeError: 这时就要考虑添加一个公共函数或属性来重构这个API,相比于添加if语句,重构将是更好的选择。

一、面向对象 - 封装

首先介绍封装的概念,封装是指将数据和行为打包到一个类中,并可以控制外部访问的级别。封装可以保护数据和方法的访问,并且提高代码的可维护性。我们可以利用访问控制修饰符来实现封装,包括公有、私有、受保护和包访问等四种级别。

简单的来说其实就是, 封装就是不让人靠近自己的东西,保护自己的隐私

class Person:
  def __init__(self, name, age):
    self.name = name
    self.age = age
 
  def get_name(self):
    return self.name
 
  def get_age(self):
    return self.age
 
  def set_name(self, name):
    self.name = name
 
  def set_age(self, age):
    self.age = age
 
person = Person("ZT", 20)
person.set_name("Tim")
print(person.get_name()) # 输出 Tim

隐式的基类 - object

每个Python类的定义都会隐式继承自 object 类,它的定义非常简单,几乎什么行为都不包括。我们可以创建一个object 实例,但很多事情无法完成,因为很多特殊方法的调用程序都会抛出异常。

对于任何自定义类,都会隐式继承object 。以下是一个类定义的示例(隐式继承了object 类)。

''' 面向对象的具体体现 '''
''' 普通函数 '''
std1 = {'name': '小明', 'score': 98}
std2 = {'name': '小红', 'score': 81}

def func(std1):
    print(f"{std1['name']}")  # 只是在函数内部打印结果
    return std1['name']  # 把函数内部数据返回给函数外部实现数据共享

print(func(std1))  # 实参为准

''' 创建对象 '''
# 在我们使用面向对象编程的时候一般是分为2步骤
# 1.先用Python类的封装
# 2.通过类创建对象
# 如何去创建类
# class 是创建类的关键字    Student---> 类的名字 类名的首字母一定要大写
class Student:
    """
    类有属性:
         划分类的时候, 对于里面的所有对象 都是有一些相同属性
         实例属性和类属性
    类有方法:
         在类中所有的对象    能够做什么  行为方法(也就是我们自己封装的函数)
    """
    # 函数在类中是具有特殊功能的函数  --> 初始化函数
    # 在后续使用类创建对象的时候, 这个函数会默认的被调用
    # 在初始化方法中, 创建的属性, 叫做实例属性
    def __init__(self, name, age):
        self.name = name  # 名字属性 绑定到对象里面去
        self.age = age  # 年龄属性 绑定到对象里面去
        # self.list1 = []

    # 在类中定义的其他函数,都叫做类的方法函数
    # 在类中,可以通过 self 调用初始化函数中的实例属性
    def print_info(self):
        print(f'名字:{self.name},年龄:{self.age}')

    def eat(self):
        self.print_info()  # 在2个方法函数之间调用
        return f'{self.name}正在吃东西'

# """基于封装的学生类, 创建学生对象""" 实例化对象
# 通过类的实例化
student1 = Student('泽言', '18')  # 创建了一个实例化对象
print(student1)
# 2. 对象操作 通过类的实例化去调用实例属性
print(student1.name)
print(student1.age)
# 去调用方法函数
print(student1.print_info())

# """在类中, 基于类可以实例化多个对象"""  (理论上是可以创建无数个的)
student2 = Student('丸子', '20')
print(student2.name)
print(student1.eat())


''' 没有参数的初始化函数 '''
# """ 泽言养了一只猫 """
class Cat:
    def __init__(self,name):
        """ 实例属性 - 定值 """
        self.name = '加菲猫'  # 绑定了是谁那么他就是谁
        self.limbs = '4'
        self.behavior = '喵喵叫'

    # 方法函数
    def print_info(self):
        return f'猫的名字:{self.name},正在吃鱼'

# 对类进行实例化
# 如果 __init__ 中没有参数, 那么实例化对象的时候就不能传参
cat = Cat('波斯猫')
print(cat.name)
print(cat.print_info())
print(Cat().name)


''' 有参数的初始化函数 '''
# """ 泽言养了一只猫 """
class Cat:
    def __init__(self, name, limbs, behavior):
        """实例属性(定值)"""
        self.name = name
        # self.limbs = 7    # 他是以绑定的参数为准的
        self.limbs = limbs
        self.behavior = behavior

    # 方法函数
    def print_info(self):
        return f'猫的名字:{self.name}'

# 对类进行实例化
cat = Cat('加菲猫', 4, '吃披萨')  # 用于代替作用
print(Cat('加菲猫', 4, '吃披萨').limbs)
print(cat.name)
print(cat.limbs)
print(cat.print_info())


''' self 指代对象 '''
# 创建一个游戏英雄类
class Hero:
    def __init__(self, name, weapon, equipment, blood):
        self.name = name
        self.weapon = weapon
        self.equipment = equipment
        self.blood = blood
        
    # 攻击方法
    def attack(self):
        return f'{self.name}'
    def res(self):
        return self  # 这个返回self本身   # 在类中, self指代的就是实例对象本身

# 实例化
hero1 = Hero('黄忠', '弓箭', ['头盔', '靴子'], 100)
hero2 = Hero('刘备', '剑', ['头盔', '盔甲'], 100)

# 调用他方法函数
a1 = hero1.res()  # 返回的就是 self 本身
print(id(a1))  # 2个id地址一样,就代表这他们2个是引用的同一个对象
print(id(hero1))

# 就是用那个实例化对象去调用,那么 self 返回的就是哪一个实例化对象
a2 = hero2.res()  # 返回的结果就是self
print(id(a2))
print(id(hero2))


''' 带参数的方法函数 '''
# 创建一个游戏英雄类
class Hero:
    def __init__(self, name, weapon, equipment, blood):
        self.name = name
        self.weapon = weapon
        self.equipment = equipment
        self.blood = blood

    # 攻击方法
    def attack(self, who, attr):
        # who 代表了 hero2 这个实例化对象
        print(f'{self.name}攻击了{who.name}') # attr攻击
        who.blood -= attr


# 实例化
hero1 = Hero('黄忠', '弓箭', ['头盔', '靴子'], 100)
hero2 = Hero('刘备', '剑', ['头盔', '盔甲'], 100)

# 把hero2实例化对象当作参数传递
hero1.attack(hero2, 20)
print(hero2.blood)


''' 类属性和类方法 '''
class Hero:  # 类的名字叫做类对象
    """在类名下定义的变量就叫做类属性"""
    name1 = '泽言'
    age2 = '18'

    def __init__(self, name, weapon, equipment, blood):
        self.name = name
        self.weapon = weapon
        self.equipment = equipment
        self.blood = blood

    # 类方法
    @classmethod  # 声明他是一个类方法
    def boat(cls):
        return cls.name1  # 通过类方法调用类属性

    # 静态方法
    # @staticmethod  声明他是一个静态方法
    @staticmethod  
    def fun():
        return '泽言正在上课'

    def attack(self):
        return f'{self.name}'


# 实例化
hero1 = Hero('黄忠', '弓箭', ['头盔', '靴子'], 100)
hero2 = Hero('刘备', '剑', ['头盔', '盔甲'], 100)

# """调用类属性的方式"""
# 通过实例化对象调用,默认传递 self 参数
print(hero1.name1)
print(hero2.name1)

# 通过类对象调用
print(Hero.name1)
print(Hero.age2)

# 通过类对象修改类属性, 会修改所有实例化对象的类属性
Hero.age2 = 25
Hero.name1 = '丸子'
print(hero1.name1)
print(hero2.name1)

# """ 调用类方法 """
# 通过实例化对象调用
print(hero1.boat())
print(hero2.boat())
# 通过类对象调用类方法
print(Hero.boat())

# """ 调用静态方法 """
# 通过实例化对象调用
print(hero1.fun())
print(hero2.fun())
# 通过类对象调用静态方法
print(Hero.fun())

# 偏门方法,做一个了解
# 通过类对象调用方法函数
print(Hero.attack(hero2))  # Hero类对象调用方法函数缺少self


''' 私有属性和私有方法 '''
class Woman:
    def __init__(self, name, weight, high, age):
        self.name = name
        self.__weight = weight
        self.high = high
        self.__age = age  # 如果加了__就表示他是一个私有属性

    # 间接访问
    def get_age(self):  # 在内部定义一个方法函数去访问私有属性
        return self.__weight
    def info_print(self):
        return f'她的身高是{self.high}'
    
    # 私有方法
    def __tts(self):  # 已经变成了私密方法
        return self.name
    def get_tts(self):
        return self.__tts()


# 实例化对象
won1 = Woman(name ='小红',high = '170cm',weight ='60kg',age='18') # 不管是位置参数,还是关键字参数都是可以的
won1 = Woman('小红', '60kg', '170cm', '18')
print(won1.get_age())
print(won1.weight)
print(won1.get_tts())

# 暴力方法,从外部直接访问,用的很少很少的
# 访问一个属性
print(won1._Woman__age)
# 访问一个方法函数
print(won1._Woman__tts())

装饰器

# 作用:可以将这个方法通过属性的形式调用
# """ 泽言养了一只猫 """
class Cat:
    def __init__(self):
        """实例属性(定值)"""
        self.name = '加菲猫'
        self.limbs = '4'
        self.behavior = '喵喵叫'

    # 方法函数
    @property  # 作用: 可以将这个方法通过属性的形式调用,仅限于类中的函数没有参数的时候才可以这样用
    def print_info(self):
        return f'猫的名字:{self.name}'

# 对类进行实例化
cat = Cat()
print(cat.name)
print(cat.print_info)
案例 - 实例化对象
"""
创建一个游戏英雄类
分别有以下属性, 初始化函数  __init__ 并且进行绑定
    名字(name),武器(weapon),装备(equipment),血量(blood)  绑定到初始化里面去

每个英雄类都有游戏技能,分别为(行为)
  攻击(attack),      # 自己封装的方法函数

创建两个英雄
# 创建多个实例化对象
    '黄忠', '弓箭', ['头盔', '靴子'], 100
    '刘备', '剑', ['头盔', '盔甲'], 100
"""
# 创建一个游戏英雄类
class Hero:
    def __init__(self, name, weapon, equipment, blood):
        self.name = name
        self.weapon = weapon
        self.equipment = equipment
        self.blood = blood

    # 攻击方法
    def attack(self):
        return f'{self.name}发动了攻击'

# 实例化
hero1 = Hero('黄忠', '弓箭', ['头盔', '靴子'], 100)
hero2 = Hero('刘备', '剑', ['头盔', '盔甲'], 100)

# 我现在要把刘备的头盔取出来
print(hero2.equipment[0])
# 要取黄忠的靴子
print(hero1.equipment[1])
# 调用他方法函数
print(hero1.attack())
案例 - 搬家具
"""
1、创建一个房子类,
+ 分别有以下属性  --  初始化函数 __init__
  名字(name), 房子面积(area), 家具<列表>(furniture)

+ 每个房子都有添加家具的方法  --  方法函数
    # 如果 家具占地面积 <= 房子剩余面积:可以搬入(家具列表添加家具名字数据并房子剩余面积更新:
    # 房屋剩余面积 - 该家具的占地面积 = 目前房子的面积
    # 否则:提示用户家具太大,剩余面积不足,无法容纳

2、实例化房子操作
    实例化一个房子, 面积50平米
    添加家具:(沙发,占地15平米)
    添加家具:(餐桌,占地8平米)
    添加家具:(大床,占地20平米)
    添加家具:(浴缸,占地8平米)
"""
class House:
    def __init__(self, name, area):
        self.name = name
        self.area = area
        self.furniture = []  # 定义空的列表,后面的方法函数需要用的就可以通过 self 进行调用

    def add_jiaju(self, item):
        if item[1] <= self.area:  # 加入家具面积小于房子的总面积那么就可以添加
            self.furniture.append(item[0])  # 把家具名字添加到列表里面去
            self.area -= item[1]  # 每次添加家具,房子的剩余面积更新
        else:
            print('提示用户家具太大,剩余面积不足,无法容纳')

# 实例化
house = House('空中花园', 50)
house.add_jiaju(['沙发', 15])
house.add_jiaju(['浴缸', 5])

print(house.area)
案例 - 英雄类
"""
1、创建一个游戏英雄类,
+ 分别有以下属性  __init__ 初始化函数
  名字(name),武器(weapon),装备(equipment), 攻击力(power),血量(blood),怒气(anger)
+ 每个英雄类都有游戏技能,分别为(行为)
  攻击(attack),放大招(nirvana)

用游戏英雄类创建三个游戏人物,分别是(属性):
    - '韩信','弓箭', ['头盔', '靴子'], 15, 100, 0
    - '刘备', '剑', ['头盔', '盔甲'], 20, 100, 0
    - '李白','长枪',['盔甲', '马'], 30, 100, 0

每个人都有游戏技能,分别为(行为):
- 攻击
  调用一次 怒气 `+2` (修改anger值)
- 放大招
  怒气值满时自动放大招,需要我们自己去设置的,一个怒气值满的临界点
"""
class Hero:
    def __init__(self, name, weapon, equipment, power, blood, anger):
        self.name = name
        self.weapon = weapon
        self.equipment = equipment
        self.power = power
        self.blood = blood
        self.anger = anger
        self.max_anger = 100  # 如果 anger 达到100,才可以释放大招

    # 释放大招
    def dazhao(self):
        self.anger = 0  # 释放完大招,怒气值清0
        print(f'{self.name}释放大招!!')

    def attack(self):
        print(f'{self.name}发动了攻击')
        self.anger += 2  # 每进行一次攻击 怒气值+2
        if self.anger == self.max_anger:  # 达到临界值就可以进行大招的释放
            self.dazhao()

# 实例化
hero1 = Hero('韩信', '弓箭', ['头盔', '鞋子'], 15, 100, 98)

hero1.attack()
print(hero1.anger)

二、面向对象 - 继承

继承是指一个类可以从父类中继承方法和属性。如果父类的某个属性或方法也在子类中定义了,那么子类会重写父类的属性或方法,这样子类可以更好地针对自己的特定要求来实现相应的功能。有人说继承可以使代码更加简洁,但是过度的继承反而会使代码变得非常复杂。---- 继承其实就像是血缘关系,你可以从你的祖先那里继承一些好的特点,但也不能完全成为他们

单继承

在Python中,单继承是指一个子类只能继承一个父类的属性和方法。Python中的类可以通过继承来扩展父类的功能,并添加自己的属性和方法。

在这里插入图片描述

''' 单继承 '''
# 在我们python里面 继承是发生在父类 和 子类之间

# 父类
class A(object):  # 当前 object 是所有类祖宗    默认继承 object  基类
    def __init__(self, name):
        self.name = name
        self.num = 2
    # 定义方法函数
    def print_info(self):
        print(self.name)
        return f'我是{self.num}'

# 子类
class B(A):  # 子类继承父类  B在继承A
    pass  # 他是可以进行一个站位, 不做任何操作

# 实例化
b = B('无敌')   # 父类 __init__ 有参数,实例化时候就要传递参数
print(b.num)
print(b.print_info())
# 类的继承关系中, 子类会将父类的实例属性和方法继承过来

''' 示例1 '''
# 父类
class Master:
    def __init__(self):
        self.secret = '[古法煎饼果子配方]'
    def make_cake(self):
        return f'运用了{self.secret}去制作煎饼果子'

# 子类
class Apprentice(Master):
    pass

# 实例化徒弟类,会把师傅类的方法和属性都继承过来
zeyan = Apprentice()
print(zeyan.secret)
print(zeyan.make_cake())


''' 示例2 '''
# 狮子(父类)
class Lion:
    def __init__(self, name, high, weight):
        self.name = name
        self.high = high
        self.weight = weight

    def eat(self):
        return f'{self.name}正在吃东西'

# 猫(子类)
class Cat(Lion):
    # pass
    def __init__(self, name, high, weight):
        # super() 可以调用父类的属性 ,适用于后面的扩展
        # (如果我想在子类添加一个自己得属性,而不去修改父类)
        # 通过super绑定我们的属性,绑定到当前对象里面去
        super().__init__(name, high, weight)

# 实例化
cat = Cat('辛巴', '50cm', '80kg')
print(cat.name)
print(cat.high)
print(cat.eat())

''' 示例3 '''
class Animal:
    def __init__(self, name):
        self.name = name
    
    def eat(self):
        print(self.name + " is eating.")

class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name)
        self.breed = breed
    
    def bark(self):
        print(self.name + " is barking.")

dog = Dog("Tom", "Husky")
dog.eat()  # 输出"Tom is eating."
dog.bark()  # 输出"Tom is barking."

多继承

多继承是指一个类可以同时继承多个父类的特性和方法。

多继承的语法非常简单,只需要在定义类时在类名后面添加括号,括号中写上要继承的所有父类的名称,用逗号隔开即可。

''' 多继承 '''
class A:
    def method1(self):
        print("Method 1 of A called.")


class B:
    def method2(self):
        print("Method 2 of B called.")


class MyClass(A, B):
    def method3(self):
        print("Method 3 of MyClass called.")


my_object = MyClass()
my_object.method1()  # 输出"Method 1 of A called."
my_object.method2()  # 输出"Method 2 of B called."
my_object.method3()  # 输出"Method 3 of MyClass called."

''' 示例 '''
# 师傅类
class Master:
    def __init__(self):
        self.secret = '[古法煎饼果子配方]'

    def make_cake(self):
        return f'运用了{self.secret}去制作煎饼果子'


# 学校类
class School:
    def __init__(self):
        self.secret = '[青灯煎饼果子配方]'

    def make_cake(self):
        return f'运用了{self.secret}去制作煎饼果子'


# 定义徒弟类(子类)
# 如果一个类继承多个类 会有优先继承第一个父类的同名的属性和方法
class Apprentice(Master, School):  # 继承关系,继承优先
    pass


# 实例化
# 实例化徒弟类  我会把师傅类的方法和属性都继承
zeyan = Apprentice()
print(zeyan.secret)
print(zeyan.make_cake())

''' 名字步一样的多继承 '''
# 师傅类(父类)
class Master:
    def __init__(self):
        self.secret = '[古法煎饼果子配方]'

    def make_cake1(self):
        return f'运用了{self.secret}去制作煎饼果子1'


# 学校类(父类)
class School:
    def __init__(self):
        self.secret = '[青灯煎饼果子配方]'

    def make_cake(self):
        return f'运用了{self.secret}去制作煎饼果子'


# 定义徒弟类(子类)
# 如果一个类继承多个类 会有优先继承第一个父类的同名的属性和方法
# 如果说我们的第一个父类 没有和调用相同的实例属性或者方法函数名字  那么他的实例属性不会往后面查找继承
# 但是方法函数会往后面查找
class Apprentice(School, Master):
    pass


# 实例化
# 实例化徒弟类  我会把师傅类的方法和属性都继承
zeyan = Apprentice()
# print(zeyan.secret1)
print(zeyan.make_cake1())

子类调用父类的方法和属性

''' 子类调用父类的方法和属性 '''
# 定义一个父类
class Master:
    def __init__(self):
        self.secret = '[古法煎饼果子配方]'

    def make_cake(self):
        return f'运用了{self.secret}去制作煎饼果子'


# 学校类
class School:
    def __init__(self):
        self.secret = '[青灯煎饼果子配方]'

    def make_cake(self):
        return f'运用了{self.secret}去制作煎饼果子'


# 定义徒弟类(子类)
# 如果一个类继承多个类 会有优先继承第一个父类的同名的属性和方法
class Apprentice(School, Master):
    # 如果是要重写, 那么方法的名字和父类的名字要是一样
    # 如果自己有的方法或者属性那么就调用自己的,
    def __init__(self):
        self.secret = '[独创煎饼果子配方]'

    def make_cake(self):
        return f'运用了{self.secret}去制作煎饼果子'

    # 调用(学校类)的方法和属性
    def school_make_cake(self):
        # 类对象就是我们类的名字
        School.__init__(self)  # 类对象调用方法他是不会默认传递 self,一定要写 self(固定方法)
        print(School.make_cake(self))

    # 调用(师傅类)的方法和属性
    def master_make_cake(self):
        Master.__init__(self)  # 类对象调用方法他是不会默认传递self 调用 实例属性
        print(Master.make_cake(self))

# 实例化
# 实例化徒弟类  我会把师傅类的方法和属性都继承
zeyan = Apprentice()
print(zeyan.secret)
print(zeyan.make_cake())

# 只有当前子类什么都没有的情况下才会去继承第一个父类同名方法和属性
print(zeyan.school_make_cake())
print(zeyan.master_make_cake())

子类重写父类属性和方法

''' 重写属性和方法 '''
# 师傅类(父类)
class Master:
    def __init__(self):
        self.secret1 = '[古法煎饼果子配方]'

    def make_cake(self):
        return f'运用了{self.secret1}去制作煎饼果子'


# 学校类(父类)
class School:
    def __init__(self):
        self.secret = '[青灯煎饼果子配方]'

    def make_cake(self):
        return f'运用了{self.secret}去制作煎饼果子'


# 徒弟类(子类)
# 如果一个类继承多个类 会有优先继承第一个父类的同名的属性和方法
class Apprentice(School, Master):
    # 如果是要重写, 那么方法的名字和父类的名字要是一样
    def __init__(self):
        self.secret = '[独创煎饼果子配方]'

    # 如果自己有的方法或者属性那么就调用自己的
    def make_cake(self):
        return f'运用了{self.secret}去制作煎饼果子'


# 实例化
# 实例化徒弟类  我会把师傅类的方法和属性都继承
zeyan = Apprentice()
print(zeyan.secret)
print(zeyan.make_cake())
# 只有当前子类什么都没有的情况下才会去继承第一个父类同名方法和属性
# print(zeyan.secret1)

使用 super() 调用父类的方法和属性

''' 使用 super() 调用父类的方法和属性 '''
# super() 一般用于单继承
# 父类
class Master:
    def __init__(self):
        self.secret = '[古法煎饼果子配方]'

    def make_cake(self):
        return f'运用了{self.secret}去制作煎饼果子'


# 子类
class Apprentice(Master):
    def __init__(self):
        self.secret = '[独创煎饼果子配方]'

    def make_cake(self):
        # super().__init__()  # 是调用父类的属性 super他是会默认传递self
        print(super().make_cake())  # 是调用父类的方法


# 实例化
# 实例化徒弟类  我会把师傅类的方法和属性都继承
zeyan = Apprentice()
# print(zeyan.secret)
print(zeyan.make_cake())

多层(重)继承

在Python中,一个类可以同时继承多个父类,这被称为多重继承。多重继承可以形成多层继承,即一个类继承了另一个类,而另一个类又继承了另一个类,以此类推。

''' 多层继承 '''
class A:
    def method1(self):
        print("Method 1 of A called.")

class B:
    def method2(self):
        print("Method 2 of B called.")

class C(A, B):
    def method3(self):
        print("Method 3 of C called.")

my_object = C()
my_object.method1()  # 输出"Method 1 of A called."
my_object.method2()  # 输出"Method 2 of B called."
my_object.method3()  # 输出"Method 3 of C called."

''' 示例 '''
# 定义一个父类
class Master:
    def __init__(self):
        self.secret = '[古法煎饼果子配方]'

    def make_cake(self):
        return f'运用了{self.secret}去制作煎饼果子'

# 学校类
class School:
    def __init__(self):
        self.secret = '[青灯煎饼果子配方]'

    def make_cake(self):
        return f'运用了{self.secret}去制作煎饼果子'

# 定义徒弟类(子类)
# 如果一个类继承多个类 会有优先继承第一个父类的同名的属性和方法
class Apprentice(School, Master):
    # 如果是要重写, 那么方法的名字和父类的名字要是一样
    # 如果自己有的方法或者属性那么就调用自己的,
    def __init__(self):
        self.secret = '[独创煎饼果子配方]'

    def make_cake(self):
        return f'运用了{self.secret}去制作煎饼果子'

    # 调用学校的煎饼果子平方
    def school_make_cake(self):
        # 类对象就是我们类的名字
        School.__init__(self)  # 类对象调用方法他是不会默认传递self
        print(School.make_cake(self))

    # 师傅类
    def master_make_cake(self):
        Master.__init__(self)  # 类对象调用方法他是不会默认传递self
        print(Master.make_cake(self))


class Xiaohong(Apprentice):
    pass


# 实例化
# 实例化徒弟类  我会把师傅类的方法和属性都继承
xiaohong = Xiaohong()
print(xiaohong.secret)
print(xiaohong.make_cake())

# 只有当前子类什么都没有的情况下才会去继承第一个父类同名方法和属性
print(xiaohong.school_make_cake())
print(xiaohong.master_make_cake())

魔法函数

魔术方法也和普通方法样都是类中定义的成员方法

  • 魔术方法不需要去手动调用的,魔术方法会在某种情况下,自动触发(自动执行)
  • 魔术方法还有一个比特殊的地方:就是多数的魔术方法前后都有两个连续的下划线
  • 魔术方法不是我们自己定义的,而是系统定义好的,我们来使用
# 定义一个父类
class Master:
    def __init__(self):
        self.secret = '[古法煎饼果子配方]'

    def make_cake(self):
        return f'运用了{self.secret}去制作煎饼果子'


# 学校类
class School:
    def __init__(self):
        self.secret = '[青灯煎饼果子配方]'

    def make_cake(self):
        return f'运用了{self.secret}去制作煎饼果子'


# 定义徒弟类(子类)
# 如果一个类继承多个类 会有优先继承第一个父类的同名的属性和方法
class Apprentice(School, Master):
    # 如果是要重写, 那么方法的名字和父类的名字要是一样
    # 如果自己有的方法或者属性那么就调用自己的,
    def __init__(self):
        self.secret = '[独创煎饼果子配方]'

    def make_cake(self):
        return f'运用了{self.secret}去制作煎饼果子'

    # 调用学校的煎饼果子平方
    def school_make_cake(self):
        # 类对象就是我们类的名字
        School.__init__(self)  # 类对象调用方法他是不会默认传递self
        print(School.make_cake(self))

    # 师傅类
    def master_make_cake(self):
        Master.__init__(self)  # 类对象调用方法他是不会默认传递self
        print(Master.make_cake(self))


class Xiaohong(Apprentice):
    pass

# 实例化
# 实例化徒弟类  我会把师傅类的方法和属性都继承
xiaohong = Xiaohong()
# print(xiaohong.secret)
# print(xiaohong.make_cake())
# 只有当前子类什么都没有的情况下才会去继承第一个父类同名方法和属性
# print(xiaohong.school_make_cake())
# print(xiaohong.master_make_cake())

# 可以使用魔法方法mro去打印他的一个继承顺序
"""假设在继承关系中不知道继承的顺序"""
print(Xiaohong.__mro__)  # object 是所有类的祖宗类,最高的级别

''' 示例 - 封装日志类 '''
import time
class writelog():
    file_url = './'
    file_name = str(time.strftime('%Y年-%m月-%d日')) + '.log'
    file0bj = None

    def __init__(self):
        print('inint方法被执行')
        self.file0bj = open(self.file_url + self.file_name, 'a+', encoding='utf-8')

    def wlog(self, log):
        date = time.strftime("%Y-%m-%d %H:%M:%S")
        mylog = date + ' ' + log + '\n'
        self.file0bj.write(mylog)
        print(f'{log}已经被写入日志文件中')

    def __del__(self):
        print('析构方法被触发,文件被关闭')
        self.file0bj.close()


l = writelog()
l.wlog('今天天气很好ye')
案例 - 综合
"""
炼体 PracticeBody
    属性:     init
        名字
        血量(blood)
        体力(power)

    行为:     其实就是我们封装的一个个方法函数
        挑水(carry_water)
            挑水消耗2点体力
        砍柴(chop_wood)
            砍柴消耗3点体力

练气 PracticeMagic
    属性:
        血量
        体力
      + 灵力(magical_power)
    行为:
        挑水
            挑水消耗0.2点灵力
        砍柴
            砍柴消耗0.3点灵力
      + 御风(fly)
            御风消耗2点灵力
      + 喷火(spurt_fire)
            喷火消耗2点灵力

练神 PracticeDivine
    属性:
        血量
        体力
        灵力
      + 神力(super_power)  可以通super进行添加
    行为:
      - 挑水
      - 砍柴
        御风  fly
            御风消耗0.2点神力
        喷火
            喷火消耗0.2点神力
      + 御剑(flying_sword)
            御剑飞行消耗 2 点神力
"""
class PracticeBody:
    """练体期"""
    def __init__(self, name, blood, power):
        self.name = name
        self.blood = blood
        self.power = power
    # 挑水
    def carry_water(self):
        self.power -= 2
    # 砍柴
    def chop_wood(self):
        self.power -= 3


class PracticeMagic(PracticeBody):
    """练气期"""
    def __init__(self, name, blood, power, magical_power):  # 新增一个灵力属性
        super().__init__(name, blood, power)  # 继承父类的属性
        self.magical_power = magical_power  # 绑定灵力属性到我当前的对象里面去
    # 挑水
    def carry_water(self):
        self.power -= 2
    # 砍柴
    def chop_wood(self):
        self.power -= 3
    # 御风
    def fly(self):
        self.magical_power -= 2
    # 喷火
    def spurt_fire(self):
        self.magical_power -= 2


class PracticeDivine(PracticeMagic):
    """炼神期"""
    def __init__(self, name, blood, power, magical_power, super_power):  # 新增一个神力属性
        super().__init__(name, blood, power, magical_power)  # 继承父类的属性
        self.super_power = super_power
    # 挑水
    def carry_water(self):
        print('我现在是炼神期大佬不会这种低级技能')
    # 砍柴
    def chop_wood(self):
        print('我现在是炼神期大佬不会这种低级技能')
    # 御风
    def fly(self):
        self.super_power -= 0.2
    # 喷火
    def spurt_fire(self):
        self.super_power -= 0.2
    # 御剑风行
    def flying_sword(self):
        self.super_power -= 2


# 实例化
zeyan = PracticeDivine('泽言', 50000, 100, 20, 10)
print(zeyan.carry_water())
print(zeyan.spurt_fire())
print(zeyan.super_power)

print(zeyan.flying_sword())
print(float(zeyan.super_power))
print(PracticeDivine.__mro__)
案例 - 动物类继承
"""
请用面向对象的继承的方式实现以下类的封装:
    动物类(Animal):
        属性:name, high, weight
        行为:吃

    老虎类(Tiger):
        属性:name, high, weight
        行为:吃、老虎的狩猎技能

    狮子类(Lion):
        属性:name, high, weight
        行为:吃、狮子的狩猎技能

    狮虎兽(Liger):
        属性:name, high, weight
        行为:吃、既有老虎的狩猎技能、也有狮子的狩猎技能
"""
# 动物类
class Animal:
    def __init__(self, name, high, weight):
        self.name = name
        self.high = high
        self.weight = weight
    def eat(self):
        return f'{self.name}正在吃东西~~'  # 实现类与类之间数据共享,就要使用 return 返回

# 老虎类
class Tiger(Animal):
    # 老虎的狩猎技能
    def hunt(self):
        return f'{self.name}发动老虎的狩猎技能!!'

# 狮子类
class Lion(Animal):
    # 狮子的狩猎技能
    def hunt(self):
        return f'{self.name}发动狮子的狩猎技能!!'


# 狮虎兽
class Liger(Tiger, Lion):
    '''老虎的狩猎技能'''
    def tiger_hunt(self):
        # 通过类对象去调用, 父类有传递的参数,那么是需要通过 self 进行绑定的
        # Tiger.__init__(self, self.name, self.high, self.weight)  # self 是不会默认被传递的
        # super
        super().__init__(self.name, self.high, self.weight)  # self 是不会默认被传递的
        return super().hunt()  # 调用老虎的狩猎技能
        # super() 只适用于单继承关系,因为他只会继承第一个父类的属性和方法
        # 用于多继承,是需要使用类对象进行调用
    '''狮子的狩猎技能'''
    def lion_hunt(self):
        # 通过类对象去调用, 父类有传递的参数,那么是需要通过 self 进行绑定的
        Lion.__init__(self, self.name, self.high, self.weight)  # self 是不会默认被传递的
        return Lion.hunt(self)  # 调用狮子的狩猎技能


# 实例化
lion = Lion('狮子', '60cm', '90kg')
print(lion.hunt())
tiger = Tiger('老虎', '50cm', '90kg')
print(tiger.hunt())

# 实例化 狮虎兽
liger = Liger('狮虎兽', '90cm', '100kg')
print(liger.tiger_hunt())
print(liger.lion_hunt())

案例 - 对象继承
"""
Hero(英雄类)的基础上,新增了法师英雄,多了下面内容(考察super用法与多继承)
法师(Mage)
    增加属性:魔法值(magical 默认0,最大100)

    - 攻击:
        调用一次 怒气 `+2`
        调用一次 魔法值 `+5`

    - 放大招
      魔法值满时自动放大招

    - 第二形态
      当怒气值满时 自动切换第二形态(魔法值最大值修改为50)
"""
# 英雄类
class Hero:
    def __init__(self, name, weapon, equipment, power, blood, anger):
        self.name = name
        self.weapon = weapon
        self.equipment = equipment
        self.power = power
        self.blood = blood
        self.anger = anger
        self.max_anger = 100  # 如果 anger 达到100,才可以释放大招

    # 释放大招
    def dazhao(self):
        self.anger = 0  # 释放完大招,怒气值清0
        print(f'{self.name}释放大招!!')
    def attack(self):
        print(f'{self.name}发动了攻击')
        self.anger += 2  # 每进行一次攻击 怒气值+2
        if self.anger == self.max_anger:  # 达到临界值就可以进行大招的释放
            self.dazhao()


# 法师
class Mage(Hero):
    def __init__(self, name, weapon, equipment, power, blood, anger, magical):  # 新增魔法值属性
        super().__init__(name, weapon, equipment, power, blood, anger)  # 绑定继承父类的属性
        self.magical = magical
        self.max_magical = 100
    # 放大招
    def dazhao(self):
        print(f'{self.name}释放了大招')
    # 切换第二形态
    def second(self):
        print(f'{self.name}切换了第二形态~~~')
        self.max_magical = 50  # 魔法值改为50
    # 攻击
    def attack(self):
        print(f'{self.name}发动了攻击')
        self.anger += 2  # 每进行一次攻击怒气值 + 2
        self.magical += 5  # 魔法值 + 5

        if self.magical == self.max_magical:  # 释放大招
            self.dazhao()
        if self.anger >= self.max_anger:
            self.second()
            self.anger = 0  # 清零怒气值

# 实例化
mage = Mage('小乔', '扇子', ['衣服', '鞋子'], 10, 100, 96, 35)
mage.attack()
print('怒气值', mage.anger)
print('魔法值', mage.magical)

mage.attack()
print('怒气值', mage.anger)
print('魔法值', mage.magical)

三、面向对象 - 多态

多态是指对象可以用多种形态来引用。这样做可以使代码更加灵活,因为同样的操作可以应用于不同的类型。多态有两种实现方式,一种是基于继承的实现,在这种实现中,父类定义一些通用的方法,子类则可以重写这些方法并实现不同的功能。另一种实现方式是接口,这种实现方式可以让不同的类实现同一个接口,从而实现多态。

  • 定义:多态是一种使用对象的方式,子类重写父类方法,调用不同子类对象的相同父类方法,可以产生不同的执行结果
  • 好处:调用灵活,有了多态,更容易编写出通用的代码,做出通用的编程,以适应需求的不断变化!
''' 多态 '''
# - 定义父类,并提供公共方法
# - 定义子类,并重写父类方法
# - 传递子类对象给调用者,可以看到不同子类执行效果不同(就额是把实例化对象当作参数去传递)

# 继承: 多态一定是发生在子类和父类之间。
# 重写: 那么方法函数名要一模一样
"""
需求:
    警务人员和警犬一起工作,警犬分2种:追击敌人和追查毒品,携带不同的警犬,执行不同的工作
"""
# 父类
class Dog:
    def work(self):  # 父类提供统一的方法,哪怕是空方法
        pass


# 追击敌人的第一个犬种类(子类)
class ArmyDog(Dog):  # 继承Dog类
    def work(self):  # 子类重写父类同名方法
        print('追击敌人')


# 稽查毒品的犬类(子类)
class DrguDog(Dog):
    def work(self):
        print('稽查毒品')


# 实例化狗类
aa = ArmyDog()  # 追击敌人
bb = DrguDog()  # 稽查毒品


# 警务人员
class Person:
    # 传入不同的对象,执行不同的代码,即不同的work函数
    def work_with_dog(self, dog):  # 是需要接受不同犬种的
        dog.work()


# 实例化人
zeyan = Person()
zeyan.work_with_dog(aa)  # 就是把实例化对象当作参数去传递
zeyan.work_with_dog(bb)
案例 - 学生管理系统
"""
分为二大类别的
第一类  针对于学生来说      -----> 专门用于保存学生的信息的 比如名字 各科成绩

第二类  针对于整个系统      ------> 用于代码逻辑的书写 比如 数据的增删改查一系列操作
"""
import json


# [{},{},{},{} ]   通过for循环遍历出来的一个个字典,在通过字典的键去获取到他的值
# [对象,对象,对象] 通过for循环遍历出来的一个个对象 -- 实例化对象.属性名称去取他的值
# 第一步定义一个学生;类用于保存我们的字段信息
# 学生类
class Student:
    """ 学生对象 """
    def __init__(self, name, chinese, math, english):
        """
       :param name: 姓名
       :param chinese: 语文成绩
       :param math: 数学成绩
       :param english: 英语成绩
       """

    self.name = name
    self.chinese = chinese
    self.math = math
    self.english = english
    self.total = chinese + math + english  # 统计总分


# 实例化
student = Student('泽言', 120, 140, 110)
print(student)
print(student.name)


# 系统类
class StudentManger:
    """系统对象"""
    def __init__(self):
        self.student_list = []  # 定义一个列表用于后面一个个对象的保存

    # 他是用于整个系统对象的代码逻辑执行 执行函数  我们后面的所有操作都会写在run函数里面
    # 开关函数
    def run(self):
        # 加载文件里面的学员数据
        self.load_student()
        while True:
            # 1.程序启动,显示信息管理系统欢迎界面,并显示功能菜单
            self.show_menu()
            # 2.用户用数字选择不同的功能
            action = input('请输入你要执行的操作:')  # 字符串
            # 3.根据用户输入的序号执行不同的功能 -- 如果用户输入1,执行新建
            if action == '1':
                # 1. 新建学生信息
                print('1. 新建学生信息')
                self.add_student()

            elif action == '2':
                # 2. 显示全部信息
                print('2. 显示全部信息')
                self.show_student()

            elif action == '3':
                # 3. 查询学生信息
                print('3. 查询学生信息')
                self.search_student()

            elif action == '4':
                # 4. 删除学生信息
                print('4. 修改学生信息')
                self.modify_student()

            elif action == '5':
                # 5. 修改学生信息
                print('5. 删除学生信息')
                self.del_student()

            elif action == '0':
                print('0. 退出系统')
                self.save_student()
                # 退出系统 -- 退出循环
                break  # 进行死循环的退出

            else:  # 处理所有的非上述情况
                print('请输入正确的选择')

                
    ''' 封装一个打印欢迎界面的函数 '''
    def show_menu(self):
        str_info = """**************************************************
        欢迎使用【学生信息管理系统】V1.0
        请选择你想要进行的操作
        1. 新建学生信息
        2. 显示全部信息
        3. 查询学生信息
        4. 修改学生信息
        5. 删除学生信息

        0. 退出系统
        **************************************************"""
        print(str_info)

        
    ''' 用于加载数据的函数 '''
    def load_student(self):
        # 1. 打开文件:
        with open('students.json', mode='r', encoding='utf-8') as f:
            # 2. 读取数据:文件读取出的数据是字符串还原列表类型;[{}] 转换 [学员对象]
            student_str = f.read()
            print(student_str)
            print(type(student_str))
        # [{},{},{}]  ----> 转变成  [对象,对象,对象]
        students = json.loads(student_str)  # 将字符串转化为python对象
        # 由于我们现在使用的是类的封装,所以操作的对象形式要变为[对象,对象]
        self.student_list = [Student(stu['name'], stu['chinese'], stu['math'], stu['english']) for stu in students]
        print(self.student_list)

        
    ''' 新建学生信息 '''
    def add_student(self):
        # 1. 用户输入姓名、性别、手机号
        name = input('请输入学生的姓名:')
        chinese = int(input('请输入学生的语文成绩: '))
        math = int(input('请输入学生的数学成绩: '))
        english = int(input('请输入学生的英语成绩: '))
        # total = chinese + math + english  这一步我们学生对象里面已经有了
        # print(total)

        # 2. 创建学员对象 -- 类 ?在student类里面  根据Student类,创建学生对象
        student = Student(name, chinese, math, english)
        # 3. 将该对象添加到学员列表
        self.student_list.append(student)  # 把对象添加到列表里面去
        # [{},{},{},{}]  [对象,对象,对象]
        # students = [
        #     {'name': name, 'chinese': chinese, 'math': math, 'english': english, 'total': total}
        # ]
        # print(students)
        print('##### ----- 添加成功 ----- #####')

        
    ''' 显示学生信息的函数 '''
    def show_student(self):
        # 1. 打印表头
        print('姓名\t语文\t数学\t英文\t总分')
        # 2. 打印学员数据
        for stu in self.student_list:  # stu就是一个个对象
            print(f"{stu.name}\t{stu.chinese}\t\t{stu.math}\t\t{stu.english}\t\t{stu.total}")  # \t是一个制表符

    ''' 查询学生信息 '''
    def search_student(self):
        # 1. 用户输入目标学员姓名
        search_name = input('请输入你要查询的名字:')
        # 2. 遍历列表。如果学员存在打印学员信息,否则提示学员不存在
        for stu in self.student_list:
            # 如果遍历的学生对象中名字是要查找的学生姓名
            if stu.name == search_name:  # 满足的条件
                print('姓名\t语文\t数学\t英文\t总分')
                print(f"{stu.name}\t{stu.chinese}\t\t{stu.math}\t\t{stu.english}\t\t{stu.total}")  # \t是一个制表符
                break
        else:  # 当循环正常结束的时候 else会被执行
            print('该学生不存在, 请检查名字是否输入正确!')

    ''' 修改学生的信息 '''
    def modify_student(self):
        # 1. 用户输入目标学员姓名
        modify_name = input('请输入你要修改的学生姓名:')
        # 2. 遍历列表数据,如果学员存在修改姓名性别手机号,否则提示学员不存在
        for stu in self.student_list:
            if stu.name == modify_name:
                # 这里我们就可以输入空字符了 就解决了上次函数封装的问题
                print('如果原有的数据不想修改, 直接敲回车!')
                name = input('请重新输入学生的姓名:')
                chinese = input('请重新输入学生的语文成绩: ')
                math = input('请重新输入学生的数学成绩: ')
                english = input('请重新输入学生的英语成绩: ')
                if name:  # 空字符串的布尔判断结果为False
                    stu.name = name
                if chinese:
                    stu.chinese = int(chinese)
                if math:
                    stu.math = int(math)  # 指定键修改值
                if english:
                    stu.english = int(english)  # 指定键修改值
                stu.total = stu.chinese + stu.math + stu.english

                break
        else:  # 当循环正常结束的时候 else会被执行
            print('该学生不存在, 请检查名字是否输入正确!')

    ''' 删除学生信息的函数 '''
    def del_student(self):
        # 输入学生的姓名, 用于查找
        del_name = input('请输入您要查询的学生姓名: ')
        for stu in self.student_list:
            # 如果遍历的学生对象中名字是要查找的学生姓名
            if stu.name == del_name:
                # pop(默认是删除最后一个列表数据, 也可以指定索引在列表中删除数据)
                # remove(根据数据在列表中删除)
                self.student_list.remove(stu)  # 返回的是stu整个对象
                # students.pop(students.index(stu))
                break
        else:
            print('该学生不存在, 请检查名字是否输入正确!')

    ''' 用于保存的函数 '''
    def save_student(self):
        # 1. 打开文件
        with open('students.json', mode='w', encoding='utf-8') as f:
            # 字符串和二进制
            # [对象,对象,对象]  ----> [{},{},{},{}]
            save_student_list = [
                {'name': stu.name, 'chinese': stu.chinese, 'math': stu.math, 'english': stu.english, 'total': stu.total}
                for stu in self.student_list]

            # 列表类型转字符串
            # ensure_ascii=False  不使用默认的编码,让他正常显示中文
            # 2. 文件写入数据
            # 2.1 [字典] 转换成 [字符串]
            student_str = json.dumps(save_student_list, ensure_ascii=False)  # 转化str类型
            # print(student_str)
            # print(type(student_str))
            # 2.2 文件写入 字符串数据
            f.write(student_str)


# 实例化执行代码逻辑
StudentManger().run()
案例 - 数据模型的学生管理系统
"""
数据库
     非关系型:Redis,MongBD 等等
     关系型: MySQL、SQL Server
      增删改查的有一些操作
      以MySQL为例:
       增  Insert
       删  delete
       改  update
       查  select
        其实就是封装一个个函数,在函数里面去写代码逻辑,而关键字就是函数名,我们调用这些函数就可以
        实现它里面所对应的功能
    数据模型 都是基于模型的一个封装 自己定好了函数
"""
"""
分为几类
第一类 是代码整体逻辑的书写
第二类 数据模型的增删改查 只用于定义增删改查的函数
"""
import json

class StuDb:
    """数据模型类: 提供系统中所有关于数据的操作方法 加载数据  数据保存  数据的增删改查"""
    def __init__(self):
        self.student_list = []  # [{},{},{}] 操作对象已经发生了改变
        # 项目启动的时候,加载数据
        self.load()

    # 加载数据
    def load(self):
        """用于加载数据的函数"""
        with open('students.json', mode='r', encoding='utf-8') as f:
            student_str = f.read()

        self.student_list = json.loads(student_str)  # 将字符串转化为python对象

    # 数据增加
    def inset(self, student):  # 在其他地方调用这个函数
        self.student_list.append(student)

    # 数据删除
    def delete(self, student):
        self.student_list.remove(student)

    # 查找数据
    def search(self, name):
        for stu in self.student_list:  # 通过名字进行查找
            if stu['name'] == name:  # 找到了
                return stu  # 返回数据所在的整个字典  这里的else不写默认返回空

    # 修改数据
    def change(self, old_name, student):
        """
        :param old_name:  老的名字
        :param student:   学生对象
        :return:
        """
        old_stu = self.search(old_name)  # 如果找到了  数据所在的整个字典
        if old_stu:  # 非空即使True
            old_stu.update(student)  # 如果有相同的键就覆盖,没有则合并

    # 显示所有数据的函数
    def all(self):
        return self.student_list

    # 数据的保存
    def save(self):
        with open('students.json', mode='w', encoding='utf-8') as f:
            # ensure_ascii=False  不使用默认的编码 让他正常显示中文
            student_str = json.dumps(self.student_list, ensure_ascii=False)  # 把数据转化成str类型
            # print(student_str)
            # print(type(student_str))
            # 只能写入str/二进制
            f.write(student_str)


# 实例化
db = StuDb()
# db.save()  # 如果要调用当前类里面的任意一个方法函数,只需要db.函数()就可以调用

# 系统类
class StudentManger:
    """系统对象"""
    # def __init__(self):
    # self.student_list = []  #定义一个列表用于后面一个个对象的保存

    # 他是用于整个系统对象的代码逻辑执行 执行函数  我们后面的所有操作都会写在run函数里面
    # 开关函数
    def run(self):
        # 加载数据
        # self.load_student()
        while True:
            # 1.程序启动,显示信息管理系统欢迎界面,并显示功能菜单
            self.show_menu()
            # 2.用户用数字选择不同的功能
            action = input('请输入你要执行的操作:')  # 字符串
            # 3.根据功能选择,执行不同的功能
            if action == '1':
                print('1. 新建学生信息')
                self.add_student()
            elif action == '2':
                print('2. 显示全部信息')
                self.show_student()
            elif action == '3':
                print('3. 查询学生信息')
                self.search_student()
            elif action == '4':
                print('4. 修改学生信息')
                self.modify_student()
            elif action == '5':
                print('5. 删除学生信息')
                self.del_student()
            elif action == '0':
                print('0. 退出系统')
                self.save_student()
                break  # 进行死循环的退出
            else:  # 处理所有的非上述情况
                print('请输入正确的选择')

    # 封装一个打印欢迎界面的函数
    def show_menu(self):
        str_info = """**************************************************
        欢迎使用【学生信息管理系统】V1.0
        请选择你想要进行的操作
        1. 新建学生信息
        2. 显示全部信息
        3. 查询学生信息
        4. 修改学生信息
        5. 删除学生信息

        0. 退出系统
        **************************************************"""
        print(str_info)

    # 用于加载数据的函数
    # def load_student(self):
    #     with open('students.json', mode='r', encoding='utf-8') as f:
    #         student_str = f.read()
    #         print(student_str)
    #         print(type(student_str))
    #     # [{},{},{}]  ----> 转变成  [对象,对象,对象]
    #     students = json.loads(student_str)  # 将字符串转化为python对象
    #     # 由于我们现在使用的是类的封装,所以操作的对象形式要变为[对象,对象]
    #     # self.student_list = [Student(stu['name'],stu['chinese'],stu['math'],stu['english'])for stu in students]
    #     # print(self.student_list)

    # 新建学生信息
    def add_student(self):
        name = input('请输入学生的姓名:')
        chinese = int(input('请输入学生的语文成绩: '))
        math = int(input('请输入学生的数学成绩: '))
        english = int(input('请输入学生的英语成绩: '))
        total = chinese + math + english
        # print(total)

        # [{},{},{}]
        stu = {'name': name, 'chinese': chinese, 'math': math, 'english': english, 'total': total}
        db.inset(stu)
        # student = Student(name,chinese,math,english)
        # self.student_list.append(student) #把对象添加到列表里面去
        # [{},{},{},{}]  [对象,对象,对象]
        # students = [
        #     {'name': name, 'chinese': chinese, 'math': math, 'english': english, 'total': total}
        # ]
        # print(students)
        print('##### ----- 添加成功 ----- #####')

    # 用于显示学生信息的函数
    def show_student(self):
        print('姓名\t语文\t数学\t英文\t总分')
        for stu in db.all():  # stu就是一个个对象
            print(f"{stu['name']}\t{stu['chinese']}\t\t{stu['math']}\t\t{stu['english']}\t\t{stu['total']}")

    # 查询学生信息
    def search_student(self):
        # 输入学生的姓名, 用于查找
        search_name = input('请输入你要查询的名字:')
        for stu in db.all():
            # 如果遍历的学生对象中名字是要查找的学生姓名
            if stu['name'] == search_name:  # 满足的条件
                print('姓名\t语文\t数学\t英文\t总分')
                print(
                    f"{stu['name']}\t{stu['chinese']}\t\t{stu['math']}\t\t{stu['english']}\t\t{stu['total']}")  # \t是一个制表符
                break
        else:  # 当循环正常结束的时候 else会被执行
            print('该学生不存在, 请检查名字是否输入正确!')

    # 修改学生的信息
    def modify_student(self):
        # 输入学生的姓名, 用于查找
        modify_name = input('请输入你要修改的学生姓名:')
        for stu in db.all():
            if stu['name'] == modify_name:
                name = input('请重新输入学生的姓名:')
                chinese = int(input('请重新输入学生的语文成绩: '))
                math = int(input('请重新输入学生的数学成绩: '))
                english = int(input('请重新输入学生的英语成绩: '))
                total = chinese + math + english

                stu = {'name': name, 'chinese': chinese, 'math': math, 'english': english, 'total': total}  # 跟新数据
                stu['total'] = stu['chinese'] + stu['math'] + stu['english']
                # 修改数据
                db.change(stu['name'], stu)
                print('##### ----- 修改成功 ----- #####')
                break
        else:  # 当循环正常结束的时候 else会被执行
            print('该学生不存在, 请检查名字是否输入正确!')

    # 删除学生信息的函数
    def del_student(self):
        # 输入学生的姓名, 用于查找
        del_name = input('请输入您要查询的学生姓名: ')
        for stu in db.all():
            # 如果遍历的学生对象中名字是要查找的学生姓名
            if stu['name'] == del_name:
                # pop(默认是删除最后一个列表数据, 也可以指定索引在列表中删除数据)
                # remove(根据数据在列表中删除)
                db.delete(stu)  # 返回的是stu整个对象
                # students.pop(students.index(stu))
                print('##### ----- 删除成功 ----- #####')
                break
        else:
            print('该学生不存在, 请检查名字是否输入正确!')

    # 用于保存的函数
    def save_student(self):
        db.save()
        # with open('students.json',mode='w',encoding='utf-8') as f:
        # 字符串和二进制
        # [对象,对象,对象]  ----> [{},{},{},{}]
        # save_student_list = [{'name':stu.name,'chinese':stu.chinese,'math':stu.math,'english':stu.english,'total':stu.total}for stu in self.student_list]
        #
        # # 列表类型转字符串
        # # ensure_ascii=False  不使用默认的编码 让他正常显示中文
        # student_str = json.dumps(save_student_list,ensure_ascii=False) # 转化str类型
        # # print(student_str)
        # # print(type(student_str))
        # f.write(student_str)


# 实例化执行代码逻辑
StudentManger().run()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

My.ICBM

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值