第八章 面向对象编程 类 对象 继续 多态
学习目标 重点 了解 掌握 重点 了解 掌握 面向对象的编程思想 1 3 类和对象的概念 2 创建对象、定义类的方法 继承和多继承的方法
8.1 8.2 章节框架 使用面向对象的思想定义银行员工类 使用继承根据职位创建银行员工类的子类 ☞点击查看本小节知识架构 让IT教学更简单,让IT学习更有效 章节框架 8.1 使用面向对象的思想定义银行员工类 ☞点击查看本小节知识架构 8.2 使用继承根据职位创建银行员工类的子类 ☞点击查看本小节知识架构
8.1 定义银行员工类 让IT教学更简单,让IT学习更有效 什么是对象?
8.1 定义银行员工类 面向过程编程是先分析解决问题的步骤,然后用流程控制语句、函数把这些步骤一步一步实现出来,这种思想。
8.1 定义银行员工类 8.1.1 面向对象的编程思想 面向对象编程不再根据解决问题的步骤来设计程序,而是先分析谁参与了问题的解决,这些参与者就是对象。 面向对象编程具备三大特性一: 封装 继承 多态
8.1 定义银行员工类 [案例] 你现在是一家游戏公司的开发人员,现在需要开发一款《人狗大战》的游戏,你就思考,人狗作战,那至少需要2个角色,一个是人,一个是狗,且人和狗都有不同的技能,例如人拿棍打狗,狗可以咬人,怎么描述这种不同的角色和他们的功能呢? 你写出了这两个角色 人和狗角色定制 游戏里的人和狗都有相同的属性,具体每条狗和每个人具体信息塑一个具体的人或狗 生成具体的人和狗 狗会咬人,人会打狗,调用咬人和打狗的函数,限制人只能做人的功能,狗只能调用狗的功能 狗和人都有不同的功能,每个功能写一个函数
类名通常使用单词首字母大写的驼峰命名法。类名后面是一个() 8.1 定义银行员工类 8.1.2 类和对象 1、使用类创建实例对象 面向对象编程的基础是对象,对象是用来描述客观事物的。这些不同的角色对象之间还具备一些共同的特征。 在python中使用关键字class定义类。 【语法】 Class ClassName(): 定义类的属性和方法 类名通常使用单词首字母大写的驼峰命名法。类名后面是一个()
8.1 定义银行员工类 8.1.2 类和对象 创建完类之后就可以使用这个类来创建实例对象。 【语法】 变量=类名() 【样例1】创建一个银行员工类,创建两个银行员工实例对象employee_a和employee_b,然后在控制台输出这两个实例对象的类型。 class BankEmployee(): pass employee_a=BankEmployee() employee_b=BankEmployee() print(type(employee_a)) print(type(employee_b))
第一个参数必须 是self,这里的self代表是实例,类创建实例对象后对自身的引用。 8.1 定义银行员工类 8.1.2 类和对象 2、给类添加实例方法 完成了类的定义之后,就可以给类添加变量和方法了。 定义在类外的称为函数,定义在类内的称为类的方法。 【语法】 def 方法名(self,方法参数列表): 方法体 第一个参数必须 是self,这里的self代表是实例,类创建实例对象后对自身的引用。
8.1 定义银行员工类 【样例2】在样例1的基础上给BankEmployee类添加两个实例方法,实现员工的打卡签到和领工资两种行为。使用新的BankEmployee类创建一个员工对象,并调用他的打卡签到和领工资的方法。 class BankEmployee(): def check_in(self): print('打卡签到') def get_salary(self): print('领到这个月的工资了') employee=BankEmployee() employee.check_in() employee.get_salary()
8.1 定义银行员工类 3、构造方法和析构方法 在类中有两个非常特殊的方法: __init__()和__del __()。__init__()方法会在创建实例对象的时候自动调用, __del __()方法会在实例对象被销毁的时候自动调用。因些__init__()称为构造方法,__del __()方法称为析构方法。
8.1 定义银行员工类 【样例3】在样例2的基础上给BankEmployee类添加构造方法和析构方法,在构造方法中向控制台输出“创建实例对象,__init__()被调用”,在析构方法中向控制台输出“实例对象”被销毁,__del__()被称为析构方法。“ class BankEmployee(): def __int__(self): print('创建实例对象,__int__()被调用') def __del__(self): print('实例对象被销毁,__del__()被调用') def check_in(self): print('打卡签到') def get_salary(self): print('领到这个月的工资了') employee=BankEmployee() employee.check_in() del employee
实例变量定义在构造方法中,这样实例对象被创建时,实例变量就会被定义、赋值,可以在类的任意方法中使用。 8.1 定义银行员工类 4、类的变量 对象的属性是以变量的形式存在的,在类中可以定义的变量类型分为实例变量和类变量两种。 (1)实例变量 实例变量是最常用的变量类型。 【语法】 self.变量名=值 实例变量定义在构造方法中,这样实例对象被创建时,实例变量就会被定义、赋值,可以在类的任意方法中使用。
8.1 定义银行员工类 4、类的变量 在python中的变量不支持 只声明不赋值,所以在定义类的变量时必须给变量赋初值。常用数据类型的初值如下表所示: 变量类型 初值 数值类型 value=0 字符串 value=“” 列表 value=[] 字典 value={} 元组 value=()
8.1 定义银行员工类 【样例4】在样例3的基础上给BankEmployee类添加3个实例变量:员工姓名、员工工号、员工工资。将员工姓名赋值为“李明”,员工工号赋值为“a2567”,员工工资赋值为5000,然后将员工信息输出到控制台上。 class BankEmployee(): def __init__(self): self.name="" self.emp_num="" self.salary=0 def check_in(self): print('打卡签到') def get_salary(self): print('领到这个月的工资了') employee=BankEmployee() employee.name="李明" employee.emp_num="a2567" employee.salary=5000 print('员工信息如下:') print('员工姓名:%s'%employee.name) print('员工工号:%s'%employee.emp_num) print('员工工资:%d'%employee.salary)
8.1 定义银行员工类 【课堂练习】在样例4先创建对象再进行实例变量赋值,比较麻烦,有其它方法可以实现 同样的效果吗?
8.1 定义银行员工类 (2)类变量 实例变量是必须在创建实例对象之后才能使用的变量。 在某些场景下,希望通过类名直接调用类中的变量或希望所有类能够公有某个变量。在这种情况下就可以使用类变量。类变量相当于类的一个全局变量。类变量与实例变量不同,不需要创建实例对象就可以使用。 【语法】 Class 类名() 变量名=初始值
8.1 定义银行员工类 【样例5】创建一个可以记录自身被实例化次数的类。 class SelfCountClass(): obj_count=0 def __init__(self): SelfCountClass.obj_count+=1 def __del__(self): SelfCountClass.obj_count-=1 list=[] create_obj_count=5 destory_obj_count=2 for index in range(create_obj_count): obj=SelfCountClass() list.append(obj) print('一共创建了%d个实例对象'%(SelfCountClass.obj_count)) for index in range(destory_obj_count): obj=list.pop() del obj print('销毁部分实例对象后,剩余的对象个数是%d'%(SelfCountClass.obj_count))
8.1 定义银行员工类 总结: 类对象对应类名,是由python创建的对象,具有唯一性。 实例对象是通过类创建的对象,表示一个独立的个体。 实例变量是实例对象独有的,在构造方法内添加或在创建对象后使用、添加。 类变量是属于类对象的变量,通过类对象可以访问和修改类变量。 如果在类中类变量与实例变量不同名,也可以使用实例对象访问类变量。 如果在类中类变量与实例变量同名,那么无法使用实例对象访问类变量。 使用实例对象无法给类变量赋值,这种尝试将会创建一个新的与类变量同名的实例变量。
课堂作业 1、设计一个简单的购房商贷月供计算器类,按照以下公式计算总利息和每月还款金额: 总利息=贷款金额*利率 每月还款金额=(贷款金额+总利息)/贷款年限 贷款年限不同,利率也不同,这里假定只有如下表所示的3种年限和利率。 年限 利率 3年(36个月) 6.03% 5年(60个月) 6.12% 20年(240个月) 6.39% 要求根据输入的贷款金额和年限计算出每月的月供如下图所示。
课堂作业 2、设计Bird(鸟)类、 Fish(鱼)类,都继承来自Animal(动物)类,实现方法print_info(),输出信息,输出结果如下所示:
技能实训 《绝地求生》是现在非常热门的射击类游戏。根据面向对象的编程思想,模拟实现玩家战斗的场景。 在这个场景中,有玩家、敌人、武器3个对象,3个对象之间的关系如下: 玩家和敌人属于人类,他们的默认血量是100. 不同的武器属于不同的武器类型,杀伤力不同。玩家使用武器击中敌人后,敌人会出现掉血的行为,每次掉血量与武器的杀伤力相同。 实现玩家装备武器并使用武器攻击两次敌人的效果如下图所示。
课堂作业 1、CS游戏根据面向对象的编程思想,模拟实现一个战士开枪射击敌人的场景。在场景中,有战士(玩家)、敌人、枪三个对象,其中枪里面又包括弹夹、子弹夹共两个对象。场景中这几个对象间的关系如下: 战士和敌人均属于人类,他们默认的血量是100; 战士射击时必须保证所持枪的弹夹中有子弹,并且每射击一次,弹夹中的子弹数量就会减少一枚; 战士射出的子弹击中了敌人,敌人就会由于子弹的杀伤力而出现掉血的行为,即每击中敌人一次,敌人的血量就减少5。 战士开枪场景如下图所示。
课堂作业 创建战士对象 士兵再次开枪 创建弹夹对象 士兵开枪 创建子弹对象 士兵拿枪 弹夹上子弹 出现敌人 创建枪对象 枪安装弹夹
课堂作业 士兵和敌人类 属性:姓名、血量、枪 方法:安装子弹、安装弹夹、拿枪、开枪。 子弹类 属性:杀伤力 方法:伤害敌人(让敌人掉血) 弹夹类 属性:容量(子弹存储的最大值)、当前保存的子弹 方法:保存子弹(安装子弹)、弹出子弹(开枪的时候) 枪类 属性:弹夹(默认没有弹夹,需要安装) 方法:连接弹夹、射子弹
课堂作业 2、设计一个Circle(圆)类,包括圆心位置、半径、颜色等属性。编写构造方法和其他方法,计算周长和面积,请编写程序验证类的功能。 3、设计一个课程类,包括课程编号、课程名称、任课教师、上课地点等属性,增加构造方法和显示课程信息的方法。
8.2 使用继承创建子类 8.2.1 继承 【任务描述】本任务实现继承银行员工类,根据职位创建柜员和经理类两个子类,并给不同的子类添加对应的属性和方法。
8.2 使用继承创建子类 8.2.1 继承 继承与抽象(先抽象再继承) 抽象即抽取类似或者说比较像的部分。 抽象分成两个层次: 抽象分成两个层次: 1.将奥巴马和梅西这俩对象比较像的部分抽取成类; 2.将人,猪,狗这三个类比较像的部分抽取成父类。 抽象最主要的作用是划分类别(可以隔离关注点,降低复杂度) 继承:是基于抽象的结果,通过编程语言去实现它,肯定是 先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。
8.2 使用继承创建子类 8.2.1 继承 抽象只是分析和设计的过程中,一个动作或者说一种技巧,通过抽象可以得到类。
8.2 使用继承创建子类 1、继承 继承可以解决编程中的代码冗余问题,是实现代码重用的重要手段。继承的思想体现了软件的可重用性。 继承:新类可以在不增加代码的条件下,通过从已有的类中继承其属性和方法来充实自身,这种现象或行为称为继承。 【语法】 Class 子类名(父类类名) 定义子类的变量和方法
8.2 使用继承创建子类 【样例6】定义宠物类Pet和继承自Pet类的子类Cat类,使用Cat类创建实例对象并调用它的实例方法。Pet类定义如下: Pet包含一个实例变量:宠物主人owner。 Pet包含一个实例方法,输出宠物主人的名字。 class Pet(): def __init__(self,owner="李明"): self.owner=owner def show_pet_owner(self): print('这个宠物的主人是%s'%(self.owner)) class Cat(Pet): pass cat_1=Cat() cat_1.show_pet_owner() cat_2=Cat("赵敏") cat_2.show_pet_owner()
8.2 使用继承创建子类 【课堂练习】在样例4的基础上,根据职位创建银行员工类的两个子类——柜员类和经理类。
8.2 使用继承创建子类 子类中如何调用父类的构造方法? 实现方式是使用super()显式调用父类的构造方法。 class BankEmployee(): def __init__(self,name,emp_num,salary): self.name=name self.emp_num=emp_num self.salary=salary def check_in(self): print('工号%s,%s打卡签到'%(self.emp_num,self.name)) def get_salary(self): print('领到这个月的工资了,%d元'%(self.salary)) class BankTeller(BankEmployee): def __init__(self,name,emp_num,salary): super().__init__(name,emp_num,salary) class BankManager(BankEmployee): pass bank_teller=BankTeller("邵兵","a008",10000) bank_teller.check_in() bank_teller.get_salary()
8.2 使用继承创建子类 2、子类的变量和方法 子类除了能够继承父类的变量和方法,作为父类的扩展,子类还可以定义属于自己的变量和方法。
8.2 使用继承创建子类 【样例7】银行给经理配备了指定品牌的公务车,经理可以在需要的时候使用。本例给BankManager类添加对应的变量和方法。 class BankEmployee(): def __init__(self,name,emp_num,salary): self.name=name self.emp_num=emp_num self.salary=salary def check_in(self): print('工号%s,%s打卡签到'%(self.emp_num,self.name)) def get_salary(self): print('领到这个月的工资了,%d元'%(self.salary)) class BankManager(BankEmployee): def __init__(self,name="",emp_num="",salary=0): super().__init__(name,emp_num,salary) self.offical_car_brand="" def use_offical_car(self): print("使用%s牌的公务车出行"%(self.offical_car_brand)) bank_manager=BankManager("李光","a008",10000) bank_manager.offical_car_brand="宝马" bank_manager.use_offical_car()
8.2 使用继承创建子类 3、封装 封装是一个隐藏属性、方法与方法实现细节的过程。在面向对象编程中,希望类中的变量或方法只能在当前类中调用。这样的需求可以采用将变量或方法设置成私有的方式实现。 【语法】 私有变量.__变量名 私有方法:__ 方法名() 在变量名或方法名前加2个下划线。
8.2 使用继承创建子类 class BankEmployee(): def __init__(self,name="",emp_num="",salary=0): self.__name=name self.__emp_num=emp_num self.salary=salary def check_in(self): print('工号%s,%s打卡签到'%(self.emp_num,self.name)) def get_salary(self): print('领到这个月的工资了,%d元'%(self.salary)) def set_name(self,name): def get_name(self): return self.__name def set_emp_num(self,emp_num): if emp_num.startswith("a"): def get_emp_num(self): return self.__emp_num class BankTeller(BankEmployee): super().__init__(name,emp_num,salary) bank_teller=BankTeller("邵兵","a9607",6000) bank_teller.set_name("邵冰") bank_teller.set_emp_num("b9666") print("员工的姓名修改为%s"%(bank_teller.get_name())) print("员工的工号修改为%s"%(bank_teller.get_emp_num())) 【样例8】将银行员工类的员工工号和员工姓名两个变量改为私有,并为其添加访问/修改方法。要求员工的工号必须以字母a开头。
8.2 使用继承创建子类 4、多继承 继承能够解决代码重用的问题。但有些情况只继承一个父类仍无法解决所有的应用场景。例如:一个银行总经理同时还兼任公司董事,此时总经理就具备了经理和董事两个岗位职责。Python语言用多继承来解决这种问题。 前面所学的一个类只有一个父类的情况称为单继承。 【语法】 Class 子类类名(父类1,父类2) 定义子类的变量和方法
8.2 使用继承创建子类 【样例9】在银行中经理可以管理员工的薪资,董事可以在董事会上投票来决定公司的发展策略,总经理是经理的同时也是公司的董事。使用多继承来实现这3个类。 class BankManager(): def __init__(self): print("BankManage init") def manage_salary(self): print("管理员工薪资") class BankDirector(): def vote(self): print("董事会投票") print("BankDirector") class GeneralManager(BankManager,BankDirector): pass gm=GeneralManager() gm.manage_salary() gm.vote() 注意: 如果子类继承了多个父类且没有自己的构造 方法,则子类会按照继承列表中父类的顺序, 找到第一个定义了构造方法的父类,并继承 它的构造方法。
课堂作业 1、设计一个表示学生的(student)类,该类的属性有姓名(name)、年龄(age)、成绩(scores)(成绩包含语文、数学和英语三科成绩,每科成绩的类型为整数),另外有3个方法。 获取学生姓名的方法:get_name(),返回类型为string。 获取学生年龄的方法:get_age()方法,返回类型为int。 返回3门科目中最高的分数:get_course(),返回类型为int。 2、设计一个表示动物(Animal)的类,该类包括颜色(color)属性和叫(call)方法。再设计一个表示鱼(Fish)的类,包括尾巴(tail)和颜色(color)两个属性,及叫(call)方法。 提示:让Fish类继承自Animal类,重写__init__()和call()方法。
8.2 使用继承创建子类 8.2.2 多态 1、多态介绍 多态在编程术语中,是指一个变量可以引用不同类型的对象,并且能自动调用被引用对象的方法,根据不同的对象类型,响应不同的操作。 继承和方法重写是实现多态的技术基础。
8.2 使用继承创建子类 8.2.2 多态 2、方法重写 方法重写是当子类从父类中继承的方法不能满足子类的需求时,在子类中对父类的同名方法进行重写(覆盖),以符合需求。
8.2 使用继承创建子类 【样例10】在代码中定义狗类Dog,它有一个方法work(),代表其工作,狗的工作内容是“正在受训”;创建一个继承狗类的军犬类ArmyDog,军犬的工作内容是“追击军人”。 class Dog(): def work(self): print("正在受训") class ArmyDog(): print("追击敌人") dog=Dog() dog.work() army_dog=ArmyDog() army_dog.work()
8.2 使用继承创建子类 8.2.2 多态 3、实现多态 【样例11】在样例10的基础上,添加3个新类。 未受训的狗类UntrainedDog类,继承Dog类,不重写父类的方法。 缉毒犬类DrugDog,继承Dog类,重写work()方法,工作内容是“搜寻毒品”。 人类Person,有一个方法work_with_dog(),根据与其合作的狗的种类不同,完成不同的工作。
8.2 使用继承创建子类 class Dog(): def work(self): print("正在受训") class UntrainedDog(Dog): pass class DrugDog(Dog): print("搜寻毒品") class ArmyDog(): print("追击敌人") class Person(): def work_with_dog(self,dog): dog.work() p=Person() p.work_with_dog(UntrainedDog()) p.work_with_dog(ArmyDog()) p.work_with_dog(DrugDog())
8.2 使用继承创建子类 多态的特点总结: 可替换性:多态对已存在的代码具有可替换性。 可扩充性:多态对代码具有可扩充性。新增子类更容易获得更多功能。 接囗性:多态是父类向子类提供的一个共同接囗,由子类来具体实现。 灵活性:多态在应用中体现了灵活多样的操作,提高了使用效率。 简化性:多态简化了应用软件的代码编写和修改过程,尤其是在处理大量对象的运算和操作时,这个特点更为突出。
类方法可以通过对象名调用类方法,也可以通过类名调用类方法。 8.2 使用继承创建子类 8.2.3 类方法和静态方法 在python中,类方法和静态方法都属于类的方法。 1、类方法 可以用修饰器@classmethod来标识类方法。 【语法格式】 Class 类名: @classmethod def 类方法名(cls) 方法体 类方法的类,通过cls访问类的属性 类方法可以通过对象名调用类方法,也可以通过类名调用类方法。
8.2 使用继承创建子类 【样例12】类方法举例。 class Test(object): number=0 @classmethod def set_number(cls,new_number): cls.number=new_number Test.set_number(300) print(Test.number) test=Test() print(test.number)
静态方法可以通过对象名调用类方法,也可以通过类名调用类方法。 8.2 使用继承创建子类 2、静态方法 可以用修饰器@staticmethod来标识静态方法。 【语法格式】 Class 类名: @staticmethod def 静态方法名() 方法体 静态方法可以通过对象名调用类方法,也可以通过类名调用类方法。
8.2 使用继承创建子类 【样例13】 静态方法举例。 class Test(object): @staticmethod #静态方法 def print_test(): print('我是静态方法') Test.print_test() test=Test() test.print_test()
课堂作业 1、利用多态特性,编程创建一个手机类Phones,定义打电话方法call()。创建手机类的两个子类:苹果手机类IPhone和Android手机类APhone,并在各自类中重写方法call()。创建一个人类Person,定义使用手机打电话的方法use_phone_call()。
技能实训 1、王者荣耀是一款非常流行的即时战类游戏,里面有非常多的游戏角色可供选择。所有的角色都具有以下操作:普通攻击,技能攻击。 创建两个英雄角色: 关羽:普通攻击10,技能攻击是“单刀赴会” 吕布:普通攻击15,技能攻击是“贪狼之握”,使用时播放旁白“谁敢战我”。 创建一个控制类,能够操纵英雄角色使用普通攻击或技能攻击。使用继承和多态的方式实现,运行效果如下图所示:
技能实训 2、股票提醒系统:设置一个买卖点,达到目标价格就提醒卖出或买入。 提醒:用到第三方接囗Tushare,安装方法:pip install tushare
技能实训 1、为学校人事部门设计一个简单的人事管理程序,满足如下管理要求: 学校人员分为三类:教师、学生、职员 三类人员的共同属性是姓名、性别、年龄、部门。 教师的特别属性是职称、主讲课程。 学生的特别属性是专业、入学日期。 职员的特别属性是部门、工资。 程序可以统计学校总人数和各类人员的人数,并随着新人进入注册和离校人员注销而动态变化。、 2、为交管部门设计一个机动车辆管理系统,功能如下: 车辆类型(大客、大货、小客、小货、摩托)、生产日期、牌照号、办证日期 车主姓名、年龄、性别、住址、身份证号。
本章小结 本章小结 本章主要讲解了类的定义及类的继承。通过本章的学习,希望大家熟练掌握类的定义、创建对象的方法、继承和多继承、多态的定义方法,为后期复杂面向对象提供有力的支持。