面向对象的三大特征:封装、继承和多态
封装
-
含义:在生活中封装就是将物品包裹起来,不让看到其内部,具有保护功能
- 在程序设计中,封装是将类中的某些部分(某些属性和方法)隐藏起来,对象不能直接使用隐藏起来的属性和方法,具有保护功能
总结:隐藏对象的属性和方法实现的细节,仅对外提供公共访问方式
目的:保护隐私
-
使用方式:将属性或者方法前面加上双下划线(我们也叫私有属性或者私有方法)
属性私有化之前,用户的信息可以直接访问并修改 class User: """用户类型""" def __init__(self,name,age): """用户姓名和年龄""" self.name = name self.age = age def __str__(self): return f"我叫{self.name},今年{self.age}岁" user = User("古印",18) # print(user) #访问用户的姓名 # print(user.name) #可以修改用户的姓名 user.name = "黄志" # print(user.name) user.age = -20 print(user)
#属性私有化之后,用户信息不可以被玩不访问或者修改 class User: """用户类型""" def __init__(self,name,age): """用户姓名和年龄""" self.__name = name self.__age = age def __str__(self): return f"我叫{self.__name},今年{self.__age}岁" user = User("古印",18) # print(user) # 访问用户的姓名 # print(user.__name) #不可以访问私有属性 #修改用户的姓名其实是增加属性 user.__name = "黄志" #新增了一个属性__name # print(user.__name) # print(user) #查看对象有哪些属性 __dict__ # print(user.__dict__)
#属性私有化之后,提供公共的访问方式和修改方式 class User: """用户类型""" def __init__(self,name,age): """用户姓名和年龄""" self.__name = name self.__age = age def get_name(self): #命名规范:get_数据 """访问姓名""" return self.__name def set_name(self,name): """修改姓名""" self.__name = name def get_age(self): """访问年龄""" return self.__age def set_age(self,age): """修改年龄""" if 0<age<=100: self.__age = age else: print("年龄不符合要求") def __str__(self): return f"我叫{self.__name},今年{self.__age}岁" user = User("古印",18) print(user) print(user.get_name(),user.get_age()) user.set_name("小古") user.set_age(125) print(user)
-
私有方法:在方法前加
__
例如:send()-->__send
()-
作用:在开发过程中保护核心代码,在类的外部不能使用(对象不能调用私有方法)
class A: def __test1(self): print("--in test1--") def test2(self): self.__test1() print("--in test2--") a = A() # a.__test1() #对象不能调用私有方法 a.test2()
-
-
私有化封装后的限制
- 1.类中 可以访问
- 2.类外/对象外 不可以访问
- 3.子类/子类对象 不可以访问
- 注意:
- 1.在python中实现封装操作,不是通过权限限制,而是通过改名策略实现的
- 2.可以使用
__dict__
()查看属性(包括私有属性),在类的内部使用私有属性,python内部会自动转化为_类名__属性名
- 3.在类的外部不能给对象添加或者修改私有属性,因为不能转换为
_类名__属性名
继承
含义:让类与类直接产生父子关系,子类可以拥有父类的属性和方法(私有属性和私有方法无法继承)
-
父类和子类
- 父类:用于被继承的类,称为父类,也叫基类,或者超类
- 子类:继承其他类的类,称为子类,也叫派生类
继承的作用:可以提高代码的重用率
-
继承语法:子类声明后面的括号中添加类型,添加的类型就是当前子类继承的父类,有了继承声明,子类就包含了父类的公共属性和方法
class Vehicle: """交通工具类""" def __init__(self,brand): self.brand = brand def move(self): # """移动的方法""" print(f"{self.brand}开始移动") class Plane(Vehicle): """飞机类型""" pass # plane = Plane("波音747") # plane.move()
-
方法的覆盖
- 子类方法中定义了和父类相同的方法,我们叫做方法的覆盖(派生方法)。实例对象调用此方法的时候就会调用自己类中的方法
class Vehicle: """交通工具类""" def __init__(self,brand): self.brand = brand def move(self): # """移动的方法""" print(f"{self.brand}开始移动") class Plane(Vehicle): """飞机类型""" def move(self): print("飞机在快速移动中") plane = Plane("波音747") plane.move()
-
查看继承的父类
格式:类名.
__base__
-
注意:
1.python3中,如果一个类没有继承任何类,默认继承object类,新式类
2.object类,是python中的祖先,所有类都是从object类中继承下来
3.继承可以隔代
-
super()方法
-
子类和父类有相同的方法,如果子类想调用父类相同的方法,可以使用super()方法
1.父类名.方法名(self)
2.2.super(当前类名,self).方法名()
3.super(当前类名,self).方法名()
坦克大战 敌方坦克和我方法坦克开火方法类似但又不完全相同 把相同属性和方法定义在基本坦克类中,我方坦克和敌方坦克继承基本坦克类 把不同的代码写在自己的开火方法中,调用自己开火方法中再调用父类方法 class BaseTank: """基本坦克类""" def fire(self): """开火的方法""" print("坦克开火了,发射子弹(15行代码)") class HeroTank(BaseTank): """英雄坦克类""" def fire(self): """子类中有自己的开火技能""" print("英雄坦克,按下空格键,发射子弹(5行代码)") #调用父类中发射子弹的代码 # BaseTank.fire(self) #1.父类名.方法名(self) # super(HeroTank, self).fire() #2.super(当前类名,self).方法名() super().fire() #3.super().方法名() class EnemyTank(BaseTank): """敌方坦克""" def fire(self): """子类中有自己的开火技能""" print("敌方坦克,随机发射子弹(7行代码)") # 调用父类中发射子弹的代码 BaseTank.fire(self) hero_tank = HeroTank() hero_tank.fire() enemy_tank = EnemyTank() enemy_tank.fire()
多继承
一个子类可以继承多个父类,就是多继承,并且拥有父类的属性和方法
-
如果子列和父类有相同的方法,就会调用子类的方法
- 思考:如果不同的父类存在着相同的方法,子类对象调用父类的时候,会调用那个方法?
class Dog: """狗的类别""" def eat(self): """吃的方法""" print("吃粑粑") class God: """神仙的类别""" # def eat(self): # print("吃蟠桃") pass class Xtq(God,Dog): """神仙狗类""" pass # xtq = Xtq() # xtq.eat() # print(Xtq.mro()) # print(Xtq.__mro__)
- python会根据MRO方法解析顺序列表进行查找
- python2.3起开始C3算法,定义类需要继承的object,称之为新式类,广度优先搜索
- MRO列表遵循一下三条原则
- 1.子类会先于父类被检查
- 2.多个父类会根据他们在列表中的顺序别检查
- 3.如果对下一个类存在两个合法选择,选择第一个父类
-
-
__init--
()方法子类继承父类,如果子类不复写父类的
__init--
()方法,创建子类对象的时候会自动调用父类的__init--
()方法子类继承父类,如果复写了父类的
__init--
()方法,创建子类对象的时候不会再调用父类的__init--
()方法-
注意:如果复写了父类的
__init--
()方法,需要调用的父类__init--
()方法,会存在隐患,如果父类的初始化方法有参宿,子类初始化无参数,子类调用父类方法就会报错,所以注意传参问题#猫类和其他宠物类相比,有自己的昵称属性 class Pet: def __init__(self,age): self.age = age def __str__(self): return str(self.age) class Cat(Pet): """猫类""" def __init__(self,nickname,age): super(Cat, self).__init__(age) self.nickname = nickname def __str__(self): return f"{self.nickname},{self.age}" cat = Cat("tom",3) print(cat)
继承的好处:提高代码的复用性,可以提升项目功能的扩展性
-
综合案例:
-
宠物类
- 属性:名字,健康值
- 方法:恢复健康
-
医院类
- 属性:医院名称
- 方法:治疗宠物(调用宠物恢复健康的方法)
狗类:继承宠物类
-
猫类:继承宠物类
import time class Pet: """宠物类""" def __init__(self,name,health): self.name = name self.health = health #小于60生病 def recovery(self): """恢复健康的行为""" while self.health<60: self.health+=5 print(f"{self.name}正在快速恢复中。。。") time.sleep(1) def __str__(self): return f"{self.name}的健康值是:{self.health}" class Hospital: def __init__(self,name): self.name = name def care(self,pet): if isinstance(pet,Pet): print(f"开始治疗{pet.name}") pet.recovery() else: print("宠物医院只接收宠物") class Dog(Pet): pass class Cat(Pet): pass class Peron: def __init__(self,name,health): self.name = name self.health = health def recovery(self): while self.health < 60: self.health += 5 print(f"{self.name}正在快速恢复中。。。") time.sleep(1) cat1 = Cat("tom",40) hospital = Hospital("南沙中心医院") # hospital.care(cat1) # print(cat1) per1 = Peron("小古",45) hospital.care(per1)
抽象类
- 1.从实现方式看,抽象类和普通类的不同之处在于:抽象类中有抽象方法,给该类不能被实例化,只能被继承,而且子类必须实现抽象方法
- 2.抽象类中可以定义普通方法
- 3.使用抽象类一般是单继承
多态
1.封装:屏蔽实现细节,但对外提供公共访问方式,将功能封装成一个整体,提供简单的调用方式
2.继承:让类和类之间产生父子关系,子类可以拥有父类的属性和方式
-
3.多态:可以让某个类实现多重形态
多态的三个条件
- 1.必须存在继承关系
- 2.重写目标方法
- 3.使用子类对象调用父类方法
定义人类:可以跳舞,可以玩,在玩的过程中跳舞 #实现多态:老年人跳广场舞 class Person: """人的类型""" def dance(self): print("跳舞") def play(self): #old print("开始玩") self.dance() #old.dance() class OldMan(Person): """老年人类别""" def dance(self): print("跳广场舞") # per1 = Person() # per1.play() old= OldMan() old.play()
-
-
用户注册案例
- 网站可以注册,注册有默认的注册方式
- 分析
- 网站类:
- 方法:注册方法,调用默认的设备发送验证码
- 设备类
- 方法:生成验证码并发送
- 网站类:
class Website: """网站类型""" def register(self,device): print("开始注册。。") #调用设备发送验证码的方法 device.send("6666") input("验证码已发送,请输入:") print("注册成功") class Device: """设备类型""" def send(self,code): print("默认发送验证码:",code) #用户注册 ws = Website() device = Device() #发起注册 ws.register(device)
- 实现多态,用不同的设备注册网站,就使用相应的设备发送验证码
-
- 实现多态,用不同的设备注册网站,就使用相应的设备发送验证码
class Website: """网站类型""" def register(self,device): print("开始注册。。") #调用设备发送验证码的方法 device.send("6666") input("验证码已发送,请输入:") print("注册成功") class Device: """设备类型""" def send(self,code): print("默认发送验证码:",code) class Phone(Device): """手机注册""" def send(self,code): print("通过手机发送验证码:",code) class Email(Device): """邮箱注册""" def send(self,code): print("通过邮箱发送验证码:",code) # #用户注册 ws = Website() # device = Device() # #发起注册 phone = Phone() ws.register(phone)