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):passclass 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):passclass E(C, C2):passclass F(D, E):passf1 = 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 = nameself.sex = sexself.life_val = 100a = 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 = sexself.__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 = nameself.sex = sexself.__life_val = 100  def get_life_val(self):print("生命值还有:", self.__life_val)def got_attack(self):self.__life_val -= 20print("[%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 = nameself.sex = sexself.__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 = sexself.__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 = namedef 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 = nameself.address = addressself.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 = 0for i in self.stuff:money += i.salaryfor i in self.classes:money += i.teacher.salaryprint("总校应发工资:%s" % money)for i in self.branches:for j in i.stuff:    money += j.salaryfor j in i.classes:money += j.teacher.salaryprint("总校分校合计应发工资:%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 = 0for 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 = nameself.address = addressself.headquater = headquaterheadquater.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_nameself.class_id = class_idself.price = priceself.num_period = num_periodself.student = set()self.branchschool = branchschoolbranchschool.classes.add(self)self.teacher = Nonedef 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 = nameself.student_id = student_idself.kecheng = Nonedef join_class(self, kecheng):self.kecheng = kechengself.Number_of_hours_attended = 0kecheng.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 += 1print("学生[%s]来上课了,已上课时数[%s]" % (self.name, self.Number_of_hours_attended))def tuixue(self):shengyuxueshi = self.kecheng.num_period - self.Number_of_hours_attendedmoney = shengyuxueshi * self.kecheng.priceprint("学生[%s]退课了,剩余课时数[%s],应退学费[%s]元" % (self.name, shengyuxueshi, money))self.kecheng.student.remove(self)class Teacher():def __init__(self, name, salary, teacher_id):self.name = nameself.salary = salaryself.kecheng = Noneself.teacher_id = teacher_iddef 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 = kechengkecheng.teacher = selfprint("教师[%s]成为课程[%s]的教师!课序号[%s]" % (self.name, kecheng.class_name, kecheng.class_id))class Stuff():def __init__(self, name, salary, stuff_id):self.name = nameself.salary = salaryself.stuff_id = stuff_idself.school = Nonedef 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 = schoolprint("雇员[%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.rhkb.cn/news/268197.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【详识JAVA语言】面向对象程序三大特性之三:多态

多态 多态的概念 多态的概念&#xff1a;通俗来说&#xff0c;就是多种形态&#xff0c;具体点就是去完成某个行为&#xff0c;当不同的对象去完成时会产生出不同的状态。 多态实现条件 在java中要实现多态&#xff0c;必须要满足如下几个条件&#xff0c;缺一不可&#xf…

基于阿里云OSS上传图片实战案例

一、案例描述 基于Springboot框架实现一个上传图片到阿里云服务端保存的小案例。 二、准备工作 基于Springboot免费搭载轻量级阿里云OSS数据存储库&#xff08;将本地文本、照片、视频、音频等上传云服务保存&#xff09;-CSDN博客 三、代码 新建这两个类&#xff1a;一个…

【数据结构初阶】九、五种比较排序的讲解和实现(直接插入 \ 希尔 \ 直接选择 \ 堆 \ 冒泡 -- C语言)

相关代码gitee自取&#xff1a; C语言学习日记: 加油努力 (gitee.com) 接上期&#xff1a; 【数据结构初阶】八、非线性表里的二叉树&#xff08;二叉树的实现 -- C语言链式结构&#xff09;-CSDN博客 排序 排序的概念 所谓排序&#xff0c;就是使一串记录&#xff0c;按照…

网络编程(IP、端口、协议、UDP、TCP)【详解】

目录 1.什么是网络编程&#xff1f; 2.基本的通信架构 3.网络通信三要素 4.UDP通信-快速入门 5.UDP通信-多发多收 6.TCP通信-快速入门 7.TCP通信-多发多收 8.TCP通信-同时接收多个客户端 9.TCP通信-综合案例 1.什么是网络编程&#xff1f; 网络编程是可以让设…

Web开发学习-HTML

第一天 固定结构 如何注释&#xff1a;vs code中使用ctrl/可以达到注释这一行的效果&#xff0c;同时再次按下ctrl/&#xff0c;可以取消注释。 HTML标签的结构 例如&#xff1a;<strong>字体加粗</strong>这个就是双标签&#xff0c;<br>换行标签&#xff…

[RoarCTF 2019]Easy Calc

这题考查的是: 字符串解析特性目录读取文件内容读取 字符串解析特性详解&#xff1a;PHP字符串解析特性 &#xff08;$GET/$POST参数绕过&#xff09;&#xff08;含例题 buuctf easycalc&#xff09;_参数解析 绕过-CSDN博客 ascii码查询表&#xff1a;ASCII 表 | 菜鸟工具 …

flurl升级之后没有FlurlNewtonsoftJsonSerializer

新建NewtonsoftJsonSerializer.cs /// <summary> /// ISerializer implementation based on Newtonsoft.Json. /// Default serializer used in calls to GetJsonAsync, PostJsonAsync, etc. /// </summary> public class NewtonsoftJsonSerializer : IJsonSerial…

【贪心算法】Leetcode 455.分发饼干 376. 摆动序列 53. 最大子数组和

【贪心算法】Leetcode 455 分发饼干 376. 摆动序列【规律很多】53. 最大子数组和 455 分发饼干局部最优推全局最优&#xff1a;尽量用大饼干去满足大胃口的小朋友 376. 摆动序列【规律很多】思想&#xff1a;注意考虑一个坡度留首尾两个点、平坡、首尾 53. 最大子数组和【好思想…

18个惊艳的可视化大屏(第12辑):智慧校园与教育方向

智慧校园可视化大屏通过数据可视化技术&#xff0c;将学校各个方面的数据信息进行展示&#xff0c;可以提高信息公开透明度、优化校园管理、提高学生教育质量和提高校内活动宣传效果等。 1提高信息公开透明度&#xff1a; 通过大屏幕展示校园各个方面的数据信息&#xff0c;可…

MyBatisPlus(SpringBoot版)的分页插件

目录 一、前置工作: 1.整体项目目录结构 2.创建普通javamaven项目。 3.导入依赖&#xff0c;改造成springboot项目 4.配置启动类 5.创建service接口及其实现类 6.创建接口Mapper 7.配置数据源 8.创建数据库表 二、使用MP&#xff08;mybatisplus&#xff09;的分页插件 二、使…

COMPOSER安装使用WIN下升级PHP-V

想用TP6使用phpspreadsheet但是说我PHP版本低&#xff0c;原来是PHP7.0 composer要求至少7.4 直接修改环境变量&#xff0c;把PHP目录切换到7.4 composer升级比较简单&#xff0c;在PHP目录下CMD然后官网的命令执行下即可 下面就可以在TP根目录下执行命令安装PHPSPREADSHEET…

Docker数据集与自定义镜像:构建高效容器的关键要素

目录 博客前言 一.数据卷 1.数据卷介绍 2.实战 宿主机和容器共享目录 容器和容器之间共享目录 二.自定义镜像 1.自定义镜像介绍 2.实战 2.1自定义centos&#xff0c;具备vim及ifconfig作用 构建镜像 通过镜像运行一个容器进行测试 2.2自定义tomact&#xff08;文件为…

【IO流系列】字符流练习(拷贝、文件加密、修改文件数据)

字符流练习 练习1&#xff1a;文件夹拷贝1.1 需求1.2 代码实现1.3 输出结果 练习2&#xff1a;文件加密与解密2.1 需求2.2 代码实现2.3 输出结果 练习3&#xff1a;修改文件数据&#xff08;常规方法&#xff09;3.1 需求3.2 代码实现3.3 输出结果 练习4&#xff1a;修改文件数…

CSP-201712-2-游戏

CSP-201712-2-游戏 解题思路 初始化变量&#xff1a;定义整数变量n和k&#xff0c;分别用来存储小朋友的总数和淘汰的特定数字。然后定义了num&#xff08;用来记录当前报的数&#xff09;和peopleIndex&#xff08;用来记录当前报数的小朋友的索引&#xff09;。 初始化小朋…

无法访问云服务器上部署的Docker容器(二)

说明&#xff1a;记录一次使用公网IP 接口地址无法访问阿里云服务接口的问题&#xff1b; 描述 最近&#xff0c;我使用Docker部署了jeecg-boot项目&#xff0c;部署过程都没有问题&#xff0c;也没有错误信息。部署完成后&#xff0c;通过下面的地址访问后端Swagger接口文档…

react useMemo 用法

1&#xff0c;useCallback 的功能完全可以由 useMemo 所取代&#xff0c;如果你想通过使用 useMemo 返回一个记忆函数也是完全可以的。 usecallback(fn,inputs)is equivalent to useMemo(()> fn, inputs). 区别是:useCallback不会执行第一个参数函数&#xff0c;而是将它返…

获取当前数据 上下移动

点击按钮 上下移动 当前数据 代码 // 出国境管理 登记备案人员列表 <template><a-row><a-col span"24"><a-card :class"style[a-table-wrapper]"><!-- 出国境 登记备案人员列表 --><a-table:rowKey"records >…

sql 注入 之sqli-labs/less-6 双注入,双引号报错注入

和第五关类似&#xff0c;只不过闭合符号是双引号 1&#xff0c;查数据库 1"and%20(updatexml(1,concat(0x7e,(select%20database()),0x7e),1))%20-- 2.查表 内容有多行&#xff0c;所以使用limit依次查询 1"and%20(updatexml(1,concat(0x7e,(select%20table_nam…

【Linux系统化学习】线程概念

目录 线程的概念 线程的引出 什么是线程 理解线程比进程更加的轻量化 线程的优点 现成的缺点 线程异常 线程用途 Linux进程VS线程 线程的简单现象 线程的概念 有关操作系统的书籍或者课本都会这样描述线程&#xff1a; 线程是比进程轻量化的一种执行流线程是进程内部…

利用小蜜蜂AI智能问答ChatGPT+AI高清绘图生成图文故事案例

利用小蜜蜂AI智能问答ChatGPTAI高清绘图生成图文故事案例 这段时间利用小蜜蜂AI网站做了一些编程、绘图以及数据分析方面的案例。再过几个月&#xff0c;我的大孙子就要出生了。我要用小蜜蜂AI智能问答和AI高清绘图为大孙子生成一个1-9的数字图文故事。 小蜜蜂AI网站可以扫如…