python中的类与对象(3)

目录

一. 类的多继承

二. 类的封装

三. 类的多态

四. 类与对象综合练习:校园管理系统


一. 类的多继承

在(2)第四节中我们介绍了什么是类的继承,在子类的括号里面写入要继承的父类名。上一节我们只在括号内写了一个父类名,但其实写入多个也是可以的,这说明它同时继承了多个父类,这就是多继承。给出一段代码示例如下:

class Shenxian:
    def fly(self):
        print("神仙在飞...")

class Monkey:
    def eat_peach(self):
        print("猴子在偷吃仙桃...")

class Sunhouzi(Shenxian, Monkey):
    def fangendou(self):
        print("孙悟空在翻跟斗...")

S = Sunhouzi()
S.eat_peach()

这里Sunhouzi就同时继承了Shenxian和Monkey类,孙猴子也具有Shenxian和Monkey的方法。这段代码也印证了,如果子类没有的方法,就要去父类里面寻找同名方法。在多继承的条件下,如果多个父类都有同名方法,它首先去寻找哪个父类下的同名方法呢?

class Shenxian:
    def fly(self):
        print("神仙在飞...")
    def fight(self):
        print("神仙在打架...") 

class Monkey:
    def eat_peach(self):
        print("猴子在偷吃仙桃...")
    def fight(self):
        print("猴子在打架...")

class Sunhouzi(Monkey, Shenxian):
    def fangendou(self):
        print("孙悟空在翻跟斗...")

S = Sunhouzi()
S.fight()  # 猴子在打架...

可见它是按照括号内的顺序调用的,从左往右发现某父类有就直接调用该父类下的方法。

现在我们考虑更复杂的情况:把Monkey类下的fight下方法删除,同时写一个Organism类,让Shenxian和Monkey类都继承Organism类。此时相当于多重继承:Sunhouzi类继承了Shenxian和Monkey类,Shenxian和Monkey类又继承Organism类:

class Organism:
    def fight(self):
        print("群魔乱舞,各种生物都在打架...")

class Shenxian(Organism):
    def fly(self):
        print("神仙在飞...")
    def fight(self):
        print("神仙在打架...") 

class Monkey(Organism):
    def eat_peach(self):
        print("猴子在偷吃仙桃...")

class Sunhouzi(Shenxian, Monkey):
    def fangendou(self):
        print("孙悟空在翻跟斗...")

S = Sunhouzi()
S.fight()  # 神仙在打架...

此时我们再让孙猴子打架,得到的输出是“神仙在打架...”

表面上来看,这类似于图的广度优先遍历(关于什么是图的广度优先遍历和深度优先遍历,可以参考数据结构博客:20.图的遍历-CSDN博客),然而实际上多重继承时确定子类继承哪一个父类方法用的是C3算法。C3算法实现了三种重要特性:

  • 保持继承拓扑图的一致性。
  • 保证局部优先原则(比如A继承C,C继承B,那么A读取父类方法,应该优先使用C的方法而不是B的方法)。
  • 保证单调性原则(即子类不改变父类的方法搜索顺序)。

我们并不去详细介绍C3算法(因为没有开发人员这么去干),只给出一段代码说明多重继承时既不满足广度优先遍历也不满足深度优先遍历:

class A:
    def test(self):
        print("from A")

class B(A):
    pass

class B2:
    def test(self):
        print("from B2")

class C(A):
    def test(self):
        print("from C")

class C2:
    def test(self):
        print("from C2")

class D(B, B2):
    pass

class E(C, C2):
    pass

class F(D, E):
    pass

f1 = F()
f1.test()
# from C

用C3方法解析的顺序即方法解析顺序(Method Resolution Order,MRO),我们可以用mro()查看,这样我们不需要懂C3算法也可以知道它的方法解析顺序:

print(F.mro()) 
# [<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class '__main__.B2'>, <class '__main__.C2'>, <class 'object'>]

二. 类的封装

封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问要访问该类的代码和数据,必须通过严格的接口控制。封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。适当的封装可以让程式码更容易理解与维护,也加强了代码数据的安全性。封装的优点如下:

  1. 良好的封装能够减少耦合。
  2. 类内部的结构可以自由修改。
  3. 可以对成员变量进行更精确的控制。
  4. 隐藏信息,实现细节。

封装的原则是:

  1. 将不需要对外提供的内容都隐藏起来;
  2. 把属性都隐藏,提供公共方法对其访问。

以下是一个没有封装的例子,我们可以不打枪就使得人的血量发生减少:

class Person:
    def __init__(self, name, sex):
        self.name = name
        self.sex = sex
        self.life_val = 100

a = Person("zhangsan", "M")
a.life_val = 50
print(a.life_val)  # 50

为了解决这个问题我们需要对变量进行私有化,即加__,此时在外部是无法访问私有属性life_val的:

class Person:
    def __init__(self, name, sex):
        self.name = name  # 实例变量,成员变量
        self.sex = sex
        self.__life_val = 100  # 私有变量,私有属性,加__使得变量私有化

a = Person("zhangsan", "M")
print(a.__life_val)  
# AttributeError: 'Person' object has no attribute '__life_val'

但是私有属性在类的内部是可以被访问的,如:

class Person:
    def __init__(self, name, sex):
        self.name = name
        self.sex = sex
        self.__life_val = 100  
    def get_life_val(self):
        print("生命值还有:", self.__life_val)
    def got_attack(self):
        self.__life_val -= 20
        print("[%s]受到了攻击,掉了20滴血,现在生命值是[%s]" % (self.name, self.__life_val))

a = Person("zhangsan", "M")
a.get_life_val()  # 生命值还有: 100
a.got_attack()  # [zhangsan]受到了攻击,掉了20滴血,现在生命值是[80]
a.get_life_val()  # 生命值还有: 80

如此我们就可以让life_val只读,而不能对其进行外部修改。

同理,我们也可以对方法进行封装,只在类的内部进行调用:

class Person:
    def __init__(self, name, sex):
        self.name = name
        self.sex = sex
        self.__life_val = 100  
    def __breath(self):
        print(self.name,"在呼吸...")

a = Person("zhangsan", "M")
a.__breath()  # AttributeError: 'Person' object has no attribute '__breath'

如果非要在外部进行访问,则需要写:实例对象._类名+方法名,相当于解封装:

class Person:
    def __init__(self, name, sex):
        self.name = name  # 实例变量,成员变量
        self.sex = sex
        self.__life_val = 100  # 私有变量,私有属性,加__使得变量私有化

a = Person("zhangsan", "M")
print(a._Person__life_val)  # 100
# 如果直接写print(a.__life_val),则会报AttributeError

三. 类的多态

有时一个对象会有多种表现形式,比如网站页面有个button按钮,这个button的设计可以不一样(单选框、多选框、圆角的点击按钮、直角的点击按钮等),尽管长的不一样,但它们都有一个共同调用方式,就是onClick()方法。我们直要在页面上一点击就会触发这个方法。点完后有的按钮会变成选中状态、有的会提交表单、有的甚至会弹窗。这种多个对象共用同一个接口,又表现的形态不一样的现象,就叫做多态(Polymmorphism)。

Polymorphilsm is based on the greek words Poly (many) and morphism(forms), 接下来我们通过代码来演示什么是多态。

(1)通过统一函数接口实现多态

class Dog():
    def sound(self):
        print("汪汪汪...")

class Cat():
    def sound(self):
        print("喵喵喵...")

def make_sound(animal):
    animal.sound()

dog = Dog()
cat = Cat()
make_sound(cat)  # 喵喵喵...
make_sound(dog)  # 汪汪汪...

(2)通过抽象类实现多态(最常用)

假如你开发一个文本编辑器,支持多种文档类型, 在用户通过你的编辑器打开文件之前,你也不知道准备要打开的是什么类型的文件,可能是pdf,也可能是word。
假如你为每个文件类型都写一个类,每个类都通过show()方法来调用打开对应的文档,为了确保每个类都必须实现show()方法,你可以写一个抽象类Document:

class Document():
    def __init__(self, name):
        self.name = name
    def show(self):
        raise NotImplementedError("Subclass must implement abstract method")

class Pdf(Document):
    def show(self):
        print("Show Pdf contents!")

class Word(Document):
    def show(self):
        print("Show Word contents!")
    

pdf = Pdf("1.pdf")
word = Word("2.doc")
obj = [pdf, word]
for o in obj:
    o.show()

四. 类与对象综合练习:校园管理系统

设计一个培训机构管理系统,有总部、分校,有学员、老师、员工,实现具体如下需求:
1.有多个课程,课程要有定价
2.有多个班级,班级跟课程有关联
3.有多个学生,学生报名班级,交这个班级对应的课程的费用
4.有多个老师,可以分布在不同校区,上不同班级的课
5.有多个员工,可以分布在不同校区在总部可以统计各校区的账户余额、员工人数、学员人数
6.学生可以转校、退学

随便瞎写了一个,可能略有出入,如有语法错误欢迎指正!

(此部分建议先自己动手写,如果写不出来再去看别人的代码)

class School():
    def __init__(self, name, address):
        self.name = name
        self.address = address
        self.branches = set()
        self.stuff = set()
        self.classes = set()
    def print_address(self):
        print("学校[%s]的地址是[%s]" % (self.name, self.address))
    def print_classes(self):
        print("学校[%s]目前所开班型有:" % self.name)
        for i in self.classes:
            print(i.class_name)
    def print_branches(self):
        print("学校[%s]目前的分校有:" % self.name)
        for i in self.branches:
            print(i.name)
    def print_stuff(self):
        print("学校[%s]目前的雇员有:" % self.name)
        for i in self.stuff:
            print(i.name)
    def pay_roll(self):
        self.count_teacher_num()
        self.count_stuff_num()
        money = 0
        for i in self.stuff:
            money += i.salary
        for i in self.classes:
            money += i.teacher.salary
        print("总校应发工资:%s" % money)
        for i in self.branches:
            for j in i.stuff:    
                money += j.salary
            for j in i.classes:
                money += j.teacher.salary
        print("总校分校合计应发工资:%s" % money)
    def count_teacher_num(self):
        teacher_num = len(self.classes)
        for i in self.branches:
            teacher_num += len(i.classes)
        print("[%s]有教师数量[%s]"%(self.name, teacher_num))
    def count_stuff_num(self):
        stuff_num = len(self.stuff)
        for i in self.branches:
            stuff_num += len(i.stuff)
        print("[%s]有职工数量[%s]"%(self.name, stuff_num))
    def count_stu_num(self):
        stu_num = 0
        for i in self.classes:
            stu_num += len(i.student)
        for i in self.branches:
            for j in i.classes:
                stu_num += len(j.student)
        print("[%s]有学生数量[%s]"%(self.name, stu_num))

class BranchSchool():
    def __init__(self, name, address, headquater):
        self.name = name
        self.address = address
        self.headquater = headquater
        headquater.branches.add(self)
        self.classes = set()
        self.stuff = set()
    def print_address(self):
        print("学校[%s]的地址是[%s]" % (self.name, self.address))
    def print_classes(self):
        print("学校[%s]目前所开班型有:" % self.name)
        for i in self.classes:
            print(i.class_name)
    def print_stuff(self):
        print("学校[%s]目前的雇员有:" % self.name)
        for i in self.stuff:
            print(i.name)

class Kecheng():
    def __init__(self, class_name, class_id, price, num_period, branchschool):
        self.class_name = class_name
        self.class_id = class_id
        self.price = price
        self.num_period = num_period
        self.student = set()
        self.branchschool = branchschool
        branchschool.classes.add(self)
        self.teacher = None
    def print_class_information(self):
        if self.teacher == None:
            print("本课程未指定授课教师,请调用教师类方法指定授课教师!")
        else:
            print("[%s]课程基本信息:" % self.class_name)
            print("课序号:[%s]" % self.class_id)
            print("开课校区:[%s]" % self.branchschool.name)
            print("授课教师:[%s]" % self.teacher.name)
            print("本课程学时:[%s]" % self.num_period)
    def print_student(self):
        self.print_class_information()
        print("-------本课程学生名单----------")
        for i in self.student:
            print(i.name,i.student_id)

class Student():
    def __init__(self, name, student_id):
        self.name = name
        self.student_id = student_id
        self.kecheng = None
    def join_class(self, kecheng):
        self.kecheng = kecheng
        self.Number_of_hours_attended = 0
        kecheng.student.add(self)
        print("学生[%s]选报了[%s]课程,课序号:[%s]" % (self.name, kecheng.class_name, kecheng.class_id))
    def print_information(self):
        print("学生[%s], 学号[%s]" % (self.name, self.student_id))
        if self.kecheng == None:
            print("该生没有选报课程!")
        else:
            print("该生所上课程[%s], 已上课时数[%s]" % (self.kecheng.class_name, self.Number_of_hours_attended))
    def shangke(self):
        self.Number_of_hours_attended += 1
        print("学生[%s]来上课了,已上课时数[%s]" % (self.name, self.Number_of_hours_attended))
    def tuixue(self):
        shengyuxueshi = self.kecheng.num_period - self.Number_of_hours_attended
        money = shengyuxueshi * self.kecheng.price
        print("学生[%s]退课了,剩余课时数[%s],应退学费[%s]元" % (self.name, shengyuxueshi, money))
        self.kecheng.student.remove(self)

class Teacher():
    def __init__(self, name, salary, teacher_id):
        self.name = name
        self.salary = salary
        self.kecheng = None
        self.teacher_id = teacher_id
    def print_information(self):
        print("教师[%s], 教师工作证号[%s]" % (self.name, self.teacher_id))
        if self.kecheng == None:
            print("该老师不执教任何课程!")
        else:
            print("该老师所上课程[%s]" % (self.kecheng.name))
    def join_class(self, kecheng):
        self.kecheng = kecheng
        kecheng.teacher = self
        print("教师[%s]成为课程[%s]的教师!课序号[%s]" % (self.name, kecheng.class_name, kecheng.class_id))

class Stuff():
    def __init__(self, name, salary, stuff_id):
        self.name = name
        self.salary = salary
        self.stuff_id = stuff_id
        self.school = None
    def print_information(self):
        print("雇员[%s], 工作证号[%s]" % (self.name, self.stuff_id))
        if self.school == None:
            print("该雇员还没有分配校区!")
        else:
            print("所在校区[%s]" % (self.school.name))
    def join_school(self, school):
        self.school = school
        print("雇员[%s]加入[%s]校区" % (self.name, school.name))
        school.stuff.add(self)
    

school1 = School("北京总校", "北京市海淀区清华大学")
branchschool1 = BranchSchool("北京分校", "北京站1号", school1)
school2 = School("上海总校", "上海市黄浦区南京东路1号")
branchschool2 = BranchSchool("上海分校", "虹桥火车站", school2)
branchschool3 = BranchSchool("上海第二分校", "松江大学城", school2)
student1 = Student("张三", 4312784)
student2 = Student("李四", 4371289)
student3 = Student("王五", 6578584)
student4 = Student("赵八", 3421554)
student5 = Student("曹一", 1254265)
student6 = Student("操日本", 9432713)
teacher1 = Teacher("赵老憨", 15000, 5478238)
teacher2 = Teacher("李老师", 18000, 8033427)
kecheng1 = Kecheng("数据结构", 548792, 500, 45, school1)
kecheng2 = Kecheng("计算机组成原理", 431287, 500, 45, branchschool1)
stuff1 = Stuff("王莹", 20000, 412379)
stuff2 = Stuff("赵鹏", 1000, 4127982)
stuff3 = Stuff("李麻瓜", 2000, 5189312)
school2.print_branches()
school1.print_classes()
student1.print_information()
branchschool1.print_classes()
student1.join_class(kecheng1)
student2.join_class(kecheng2)
student3.join_class(kecheng1)
teacher1.join_class(kecheng1)
teacher2.join_class(kecheng2)
kecheng1.print_student()
school1.count_stu_num()
student1.print_information()
student1.shangke()
student1.shangke()
student1.tuixue()
stuff1.join_school(school1)
stuff2.join_school(branchschool1)
stuff3.join_school(school2)
school1.print_stuff()
school1.pay_roll()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/425841.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

怎么删除CSDN上发布的文章(电脑版)

怎么删除CSDN上发布的文章(电脑版) 第一步&#xff1a;回到个人主页 第二步&#xff1a;点击右上角的“创作中心” 第三步&#xff1a;点击进去之后找到“管理”——“内容管理” 第四步&#xff1a;找到要删除的文章&#xff0c;点击右侧的三个小点点 第五步&#xff1a;然后…

js优雅的统计字符串字符出现次数

题目如下 统计一串字符串中每个字符出现的频率 示例字符串 let str asdfasqwerqwrdfafafasdfopasdfopckpasdfassfd小白写法 let str asdfasqwerqwrdfafafasdfopasdfopckpasdfassfdlet result {}; for (let i 0; i < str.length; i) {if (result[str[i]]) {result[str[…

了解游戏中的数据同步

数据同步 在联机游戏中&#xff0c;我的操作和数据要同步给同一局游戏中其他所有玩家&#xff0c;其他玩家的操作和数据也会同步给我。这叫做数据同步&#xff0c;目前数据同步的方式则有帧同步和状态同步。 状态同步&#xff1a;将操作发送给服务端&#xff0c;服务端对操作…

springboot-基础-eclipse打包jar包和war包的方法与排错

目录 打jar包打war包排错获取包外位置eclipse找不到*.jar 打jar包 修改 application-dev.yml spring.thymeleaf.prefixfile:./templates/ &#xff08;非必须&#xff01;如果遇到找不到模板的情况这样做&#xff09;把templates文件夹复制到jar文件同级的目录。 但是无法解…

Linux 基础IO(1)内存文件

文章目录 铺垫文件的系统调用接口文件描述符缓冲区 铺垫 文件文件内容 文件属性访问文件之前&#xff0c;都要先打开文件&#xff0c;而要访问&#xff0c;修改&#xff0c;编辑文件&#xff0c;文件就必须加载到内存中程序运行起来变成进程&#xff0c;被CPU调度&#xff0c;…

基于YOLOv的目标追踪与无人机前端查看系统开发

一、背景与简介 随着无人机技术的快速发展&#xff0c;目标追踪成为无人机应用中的重要功能之一。YOLOv作为一种高效的目标检测算法&#xff0c;同样适用于目标追踪任务。通过集成YOLOv模型&#xff0c;我们可以构建一个无人机前端查看系统&#xff0c;实现实时目标追踪和可视化…

构建高效的接口自动化测试框架思路

在选择接口测试自动化框架时&#xff0c;需要根据团队的技术栈和项目需求来综合考虑。对于测试团队来说&#xff0c;使用Python相关的测试框架更为便捷。无论选择哪种框架&#xff0c;重要的是确保 框架功能完备&#xff0c;易于维护和扩展&#xff0c;提高测试效率和准确性。今…

循序渐进,搞懂什么是回溯算法

循序渐进&#xff0c;搞懂什么是回溯算法 回溯算法简介 回溯算法&#xff08;backtracking algorithm&#xff09;实际上是一个类似枚举的搜索尝试过程&#xff0c;主要是在搜索尝试过程中寻找问题的解&#xff0c;当发现已不满足求解条件时&#xff0c;就“回溯”返回&#…

【高数】常数项级数概念与性质

下面为个人数学笔记&#xff0c;有需要借鉴即可。 一、常数项级数概念 二、常数项级数性质 三、调和级数 完。

文件底层的深入理解之文件输入输出重定向

目录 一、文件fd的分配规则 二、对输出重定向现象的理解 三、输出输入重定向的简单实现 1、输出重定向 2、输入重定向 一、文件fd的分配规则 最小的没有被使用的数组下标&#xff0c;会被分配给最新打开的文件。 二、对输出重定向现象的理解 正如上面这段代码所示&#xff0…

IO多路复用:提高网络应用性能的利器

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

WEB APIs (5)

window对象 BOM&#xff08;浏览器对象模型&#xff09; 其为js操作浏览器提供了方法 window对象是一个全局变量&#xff0c;是BOM树根节点 BOM的属性和方法都是window的&#xff0c;如document、console.log()等 var定义在全局全局作用域中的变量、函数都会变成window对象…

138.乐理基础-等音、等音程的意义

上一个内容&#xff1a;137.乐理基础-协和音程、不协和音程 上一个内容里练习的答案&#xff1a; 等音、等音程的意义&#xff0c;首先在 19.音阶 里写了&#xff0c;一个调使用的音阶应当是从主音快开始&#xff0c;以阶梯状的形式进行到主音结束&#xff0c;这样才能明显从乐…

VMware Workstation Pro 17 虚拟机软件安装教程

VMware软件介绍 VMware Workstation是一款功能强大的桌面虚拟计算机软件&#xff0c;提供用户可在宿主机操作系统上同时运行不同的操作系统(虚拟化技术)&#xff0c;所运行的操作系统可方便的进行复制和移动&#xff0c;突破传统架构的限制。本文将以VMware Workstation Pro 1…

tomcat 反向代理 自建博客 修改状态页 等

一 自建博客 随后&#xff0c;拷贝到webapps下面 并且做软连接 随后重定向 并且下载 cat >/etc/yum.repos.d/mysql.repo <<EOF [mysql57-community] nameMySQL 5.7 Community Server baseurlhttp://repo.mysql.com/yum/mysql-5.7-community/el/7/x86_64/ enabled1 g…

excel中如何使用VLOOKUP和EXACT函数实现区分大小写匹配数据

在 Excel 中&#xff0c;VLOOKUP 函数默认情况下是不区分大小写的&#xff1a; 比如下面的案例&#xff0c;直接使用VLOOKUP函数搜索&#xff0c;只会搜索匹配到不区分大小写的第一个 如果我们想要实现区分大小写的精确匹配&#xff0c;可以使用 EXACT 函数结合 VLOOKUP 函数 …

openGauss学习笔记-234 openGauss性能调优-系统调优-资源负载管理-资源管理准备-设置控制组

文章目录 openGauss学习笔记-234 openGauss性能调优-系统调优-资源负载管理-资源管理准备-设置控制组234.1 背景信息234.2 前提条件234.3 操作步骤234.3.1 创建子Class控制组和Workload控制组234.3.2 更新控制组的资源配额234.3.3 删除控制组 234.4 查看控制组的信息 openGauss…

QT Mingw32/64编译ffmpeg源码生成32/64bit库以及测试

文章目录 前言下载msys2ysamFFmpeg 搭建编译环境安装msys2安装QT Mingw编译器到msys环境中安装ysam测试 编译FFmpeg测试 前言 FFmpeg不像VLC有支持QT的库文件&#xff0c;它仅提供源码&#xff0c;需要使用者自行编译成对应的库&#xff0c;当使用QTFFmpeg实现播放视频以及视频…

知识图谱1——neo4j

2024年要搞知识图谱&#xff0c;因此没有办法&#xff0c;只能将我之前固守的JDK1.8&#xff0c;升级到JDK21&#xff0c;因为JDK21也是LTS版本&#xff0c;neo4j高版本就不支持JDK8&#xff0c;因此没有办法&#xff0c;只有升级了。写这篇只是一个搭建笔记&#xff0c;我的初…

随机生成验证码

随机生成验证码 需求&#xff1a;随机生成一个任意位的验证码包含数字、大写字母和小写字母 1.代码实现 package com.ham;import java.util.Random;public class case2 {public static void main(String[] args) {System.out.println(code(4));}public static String code(i…
最新文章