面向对象三大特征
封装
-
封装含义:在生活中封装指的是将我们的物品包裹起来,不让看到其内部,具有保护功能。
在程序设计中,封装(Encapsulation)是将类中某些部分(某些属性或者方法)隐藏起来,对象 不能直接使用隐藏起来的属性或者方法,具有保护功能
-
总结:隐藏对象的属性和方法实现细节,仅对外提供公共访问方式
-
目的:保护隐私
-
使用方式:将属性或者方法名称钱加上双下划线(我们也叫私有属性或者私有方法)
#属性私有化之前,用户信息可以直接访问并修改
class User:
"""用户类型"""
def __init__(self,name,age):
"""用户姓名和年龄"""
self.name = name
self.age = age
def __str__(self):
"""打印用户信息"""
return f"我叫{self.name},今年{self.age}岁"
user = User("刘江",23)
# print(user)
#访问用户的姓名
# print(user.name)
#可以修改用户的姓名
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("刘江",23)
# print(user)
print(user.__name)
#修改用户的姓名和年龄
# user.__name = "梁国赞"
# user.__age = -20
# print(user.__name,user.__age)
# print(user) #还是原来的属性,并没有修改,而是增加属性
#属性私有化之后,提供公共的访问方式和修改方式
class User:
"""用户类型"""
def __init__(self,name,age):
"""用户姓名和年龄"""
self.__name = name
self.__age = age
def set_name(self,name):
"""修改姓名"""
self.__name = name
def get_name(self):
"""获取姓名"""
return self.__name
def set_age(self,age):
"""修改年龄"""
if 0<age<=100:
self.__age = age
else:
print("年龄不符合要求!")
def get_age(self):
"""获取年龄"""
return self.__age
def __str__(self):
"""打印用户信息"""
return f"我叫{self.__name},今年{self.__age}岁"
user = User("刘江",23)
# print(user)
# print(user.get_name())
# user.set_name("小江")
user.set_age(120) #设置不符合要求的年龄,无法修改
user.set_age(25) #设置符合要求的年龄,可以修改
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()
-
案例:狗生一个崽儿,休一个月产假
- 属性:孩子数量
- 方法:生孩子,休产假,生孩子后自动调用休产假的方法
class Dog: """狗的类型""" def __init__(self): self.__baby_count = 0 #定义狗崽儿的数量 def birth(self): print("生了一个崽儿") self.__baby_count+=1 #调用休产假的方法 self.__holiday() def __holiday(self): print("休一个月产假!") def __str__(self): return f"孩子数量{self.__baby_count}" dog1= Dog() dog1.birth() print(dog1) # dog1.__holiday() #无法调用私有方法 dog1.baby_count = 5 #无法修改狗崽儿的数量 print(dog1)
-
练习:打电话
- 手机类
- 方法:
- 通话方法(私有方法)
- 平台方法,检查话费余额,大于0调用通话方法
class Phone: """手机类型""" def __call(self): """打电话方法""" print("可以打电话!") def platform(self,money): if money>0: self.__call() else: print("电话已欠费,请充值后再拨。。。") phone = Phone() # phone.__call() #私有方法不可以通过对象直接调用 phone.platform(20) phone.platform(-10)
-
私有化封装后的限制
- 1.类中 可以访问
- 2.类外/对象外 不可访问
- 2.子类/子类对象 不可访问
- 注意:
- 1.在Python中实现的封装操作,不是通过权限限制而是通过改名策略实现的,名字变了找不到而已。
- 2.可以使用
__dict__
()查看属性(包括私有属性),在类内部使用的私有属性,Python内部会自动进行转换成 _类名__属性名 - 3.在类的外部不能给对象添加或者修改私有属性是因为不能转换为 _类名__属性名
class User:
"""用户类型"""
def __init__(self,name,age):
"""用户姓名和年龄"""
# self.name = name
# self.age = age
self.__name = name
self.__age = age
def __str__(self):
"""打印用户信息"""
# return f"我叫{self.name},今年{self.age}岁"
return f"我叫{self.__name},今年{self.__age}岁"
user = User("刘江",23)
# print(user)
#属性私有化之前,对象的属性如下
# print(user.__dict__) #{'name': '刘江', 'age': 23}
# user.name = "徐伟明" #可以修改
#属性私有化之后,对象的属性如下
print(user.__dict__) #{'_User__name': '刘江', '_User__age': 23}
# user.__name = "徐伟明" #只是增加属性
# print(user.__dict__)
user._User__name = "徐伟明"
print(user)
- 思考:在类属性中添加私有属性,可以访问吗?可以修改吗?
继承
-
含义:让类与类之间产生父子关系,子类可以拥有父类的属性和方法(私有属性和私有方法无法继承)
-
父类和子类
- 父类:用于被继承的类,称为父类,也叫基类,或者超类
- 子类:继承其他类的类,称为子类,也做派生类
-
继承的作用:可以提高代码的重用率
-
继承的基本语法: 子类声明后面的括号中添加的类型,就是当前子类继承的父类类型,有了继承声明,子类就包含了父类中的公共属性和方法了
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) 继承可以隔代
# 继承可以隔代 class Animal: def eat(self): print("吃东西") class Dog(Animal): # def eat(self): # print("吃骨头") pass class Hsq(Dog): pass hsq = Hsq() # hsq.eat() #查看继承的父类 print(Hsq.__base__) #<class '__main__.Dog'> print(Dog.__base__) #<class '__main__.Animal'> print(Animal.__base__) #<class 'object'>
-
-
-
super()方法
-
子类和父类有相同的方法,如果子类想调用父类相同的方法,可以使用super()方法
1.父类名.方法名(self)
2.super(当前类名,self).方法名()
3.super().fire()
#坦克大战 #敌方坦克和我方坦克开火方法类似但又不完全相同 #把相同属性和方法定义在基本坦克类中,我方和敌方坦克继承基本坦克类 #把不同的代码写在自己的开火方法中,调用自己开发方法中再调用父类开火方法 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().fire() 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("吃蟠桃") class Xtq(God,Dog): """神仙狗类""" pass xtq = Xtq() xtq.eat() #吃蟠桃 print(Xtq.mro()) #[<class '__main__.Xtq'>, <class '__main__.God'>, <class '__main__.Dog'>, <class 'object'>]
- python 会根据MRO方法解析顺序列表进行查找
- Python2.3起开始使用C3算法,定义类需要继承object,称之为新式类,广度优先搜索
- MRO列表遵循以下三条准则
- 1.子类会先于父类被检查
- 2.多个父类会根据他们在列表中的顺序被检查
- 3.如果对下一个类存在两个合法选择,选择第一个父类
class A: def func(self): print("--A--") class B(A): def func(self): super(B, self).func() print("--B--") class C(A): def func(self): super(C, self).func() print("--C--") class D(B,C): def func(self): super(D, self).func() print("--D--") d = D() #继承顺序 D-->B-->C-->A d.func() print(D.mro())
-
-
__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}" cat1 = Cat("tom",3) print(cat1)
-
-
继承的好处:可以提高代码的复用性,页可以提升项目功能的扩展性
综合案例:宠物就医,实现生病宠物去宠物医院治疗,最终康复的业务流程,具体逻辑
-
宠物类
- 属性:名字,健康值
- 方法:恢复健康
-
医院类
- 属性:医院名称
- 方法:治疗宠物(调用宠物自我恢复的方法)
-
狗类:继承宠物类
-
猫类:继承宠物类
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 Cat(Pet): """猫类""" pass class Dog(Pet): """狗类""" pass class Person: 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 = Person("小张",30) # hospital.care(per1) #使用isinstance可以判断对象是否是类的实例 # isinstance(对象,类) #可以判断数据类型 a = [1] print(isinstance(a,list)) print(isinstance(a,str))
抽象类
- 需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化
- 从实现角度看,抽象类与普通类的不同之处在于:抽象类中只能有抽象方法(没有实现功能),该类不能被实例化,只能被继承,且子类必须实现抽象方法。这一点与接 口有点类似,但其实是不同的
- 1.从实现角度来看,抽象类和普通类不同之处在于:抽象类中有抽象方法,该类不能被实例化,只能被继承,而且子类必须实现抽象方法
- 2.抽象类中可以定义普通方法
- 3.抽象类也是定义规范
- 4.使用抽象类一般是单继承
#抽象类 import abc class Person(metaclass=abc.ABCMeta): """这是一个抽象类,不能实例化""" @abc.abstractmethod def eat(self): """这是一个抽象方法,子类必须实现""" print("吃饭") def breath(self): """这是普通方法""" print("呼吸") class Student(Person): """学生类""" def eat(self): print("吃食堂") def sleep(self): print("睡宿舍") stu = Student() stu.sleep() stu.eat() stu.breath()