封装、继承、多态是面向对象的三大特征
1 封装
- 封装体现的是对于对象数据安全的保护性(对隐私数据的保护)
- 封装使属性的操作更加灵活可控,可以按照需求的描述对属性进行限制性访问,提高属性的安全性
1.1 封装(私有化)语法
命名:
- 变量使用两个下划线开头,在类型和对象的外部不能直接去访问,因为语法上该变量名称是私有的,如: __name
- 约定语法,使用一个下划线开头,约定在类型和对象外部不要直接去访问(虽然在语法上是可以访问的),如:_name
class Person:
"""类:人"""
def __init__(self, name, action):
self.name = name
self.__action = action
私有属性方法:
- 给每个属性提供set(赋值)/get(访问)方法,通过固定语法提供给外部调用者
私有属性赋值:
def set_action(self, action):
"""设置私有属性action"""
self.__action = action
# 也有写成双下划线的(一般用单下划线):
def set__action(self, action):
"""设置私有属性action"""
self.__action = action
获取私有属性数据:
def get_action(self):
"""获取私有属性action"""
return self.__action
# 双下划线写法(一般用单下划线)
def get__action(self):
"""获取私有属性action"""
return self.__action
外部需要通过set和get方法调用私有属性:
class Person:
"""类:人"""
def __init__(self, name, action):
self.name = name
self.__action = action
def set_action(self, action):
"""设置私有属性action"""
self.__action = action
def get_action(self):
"""获取私有属性action"""
return self.__action
# 创建对象
an = Person("安禄山", "叛乱")
# print(an.action) # name 'action' is not defined # 私有属性不能直接访问,会报错
print(an.get_action()) # 叛乱 # 通过get方法获取
an.set_action("胡旋舞") # 修改私有属性
print(an.get_action()) # 胡旋舞
# 用以下方法修改并不能修改原私有属性,只是新增了一个属性
an.__action = "跳舞"
print(an.get_action()) # 胡旋舞 # 属性值没有改变
set方法与__init__区别:
- __init__:给属性赋初始值,初始化对象属性数据
- set方法:给已有对象修改属性数据
1.2 限制条件(数据保护)
在set/get方法中添加条件限制
def set_action(self, action):
if 2 <= len(action) <= 10:
self.__action = action
else:
print("长度不符合要求")
def get_action(self, action):
return __action
1.3 私有类属性和私有方法
- 私有类属性用法相似,也是前面加双下划线封装,不能被外部访问,可以被类内部访问
- 类似的,私有方法是在方法前面加两个下划线
- 可用于保护核心代码,只能在类的内部调用,外部对象不能(直接)调用
def __test():
pass
2 继承
通过继承语法,让两个类型之间产生继承关系,子类可以使用父类的属性和方法,达到复用目的
2.1 继承语法
父类和子类:
- 父类:被继承的类型
- 子类:继承其他类的当前类型
- class 子类(父类)
class Person(object):
"""人的类"""
pass
class Emperor(Person):
"""皇帝的类"""
pass
- 在python3中,如果一个类没有继承任何类,默认继承object,声明时可以不写
- 子类只能继承父类的公共的属性和方法,以及默认的魔法方法。
- 子类如果不写任何魔法方法,就会继承默认固定格式的魔法方法
- 私有属性和私有方法不能被继承(理论上)
2.2 __init__()方法的继承
- 子类不编写该方法,就会默认从父类继承默认格式的该方法__init__(self)
- 子类编写了__init__()方法,默认继承的__init__()就会被回收
- 一般子类中写的__init__()方法中都会显式的调用父类的__init__(),初始化父类数据
调用父类的__init__():
# 单继承时是三种写法都可以
# __init__()中需要传递父类相应参数,不需带self
super().__init__()
父类名称.__init__() (多继承用此写法)
super(子类名称, self).__init__() # 子类名称即当前类名
子类不写__init__()方法默认继承父类的__init__()方:
class Person(object):
"""人的类"""
def __init__(self, name, position):
"""初始化类型"""
self.name = name
self.position = position
class Emperor(Person):
"""皇帝的类"""
pass
# 子类不写__init__()方法默认继承父类
li = Emperor("李隆基", "皇帝")
子类中写的__init__()方法中,都会显式的调用父类的__init__(),初始化父类数数据,否则父类的数据就不会被 正确的继承过来:
```python
class Person(object):
"""人的类"""
def __init__(self, name, position):
"""初始化类型"""
self.name = name
self.position = position
class Emperor(Person):
"""皇帝的类"""
def __init__(self, name, position, gender):
super().__init__(name, position) # 主动调用父类__init__(),单继承时三种写法皆可,不需带self
self.gender = gender # 写新增的
# 创建子类对象按照子类的的属性
li = Emperor("李隆基", "皇帝", "男")
2.3 方法重写
即子类中重新声明定义了和父类中名称和参数相同的方法
- 如果子类没有重写父类的方法,在调用执行方法时会找到并执行父类中声明的方法
class Person(object):
"""人的类"""
def run(self):
print("逃跑")
class Emperor(Person):
"""皇帝的类"""
pass
# 创建对象
li = Emperor()
# 没有重新的方法,则使用父类的方法
li.run() # 逃跑
- 如果子类中重写了父类的方法,在调用执行方法时就会找到并执行子类的方法
class Person(object):
"""人的类"""
def run(self):
print("逃跑")
class Emperor(Person):
"""皇帝的类"""
# 重写父类方法
def run(self):
print("九重城阙烟尘生,千乘万骑西南行")
# 创建对象
li = Emperor()
# 方法被重写,使用子类方法
li.run() # 九重城阙烟尘生,千乘万骑西南行
2.4 子类访问父类(功能拓展)
- 父类的方法中完成基础功能的处理
- 子类重写父类方法,调用父类的方法完成基础操作,同时扩展新的功能
三种访问形式:
super().方法名称()
super(子类名称, self).方法名称() # 子类名称即当前类名
父类名称.方法名称() # 注意是否需要带参数self,另外两种方法不需带
class Person(object):
"""宫殿类"""
def dream(self):
print("做梦")
class Emperor(Person):
"""皇帝的类"""
# 重写父类方法
def dream(self):
super().dream() # 访问类方法,并拓展功能
print("鸳鸯瓦冷霜华重,翡翠衾寒谁与共?")
print("悠悠生死别经年,魂魄不曾来入梦。")
# 创建对象
li = Emperor()
# 方法被重写,且在父类基础上拓展
li.dream()
# 做梦
# 鸳鸯瓦冷霜华重,翡翠衾寒谁与共?
# 悠悠生死别经年,魂魄不曾来入梦。
2.5 多继承
- 将继承的类型依次(有顺序)声明在类型后面的括号中,描述当前类型继承的所有类型
- 当前类型就会拥有所有继承类型的公共的属性和公共的方法
- 语法:class 子类(父类1, 父类2,父类3…)
- 当多个父类中出现了相同名称的属性和方法,子类在使用时,按照广度优先进行查询(先按继承顺序在同级父类查询,然再向上一级父类查询)
- 查询顺序:类名.mro()或类名.__mro__(),python继承顺序就是根据该方法方法解析顺序列表进行查找(子类会先于父类被检查 > 多个父类会根据他们在列表中的顺序被检查 > 如果对下一个类存在两个合法选择,选择第一个父类)
class Flower:
def test(self):
print("芙蓉如面柳如眉")
def open(self):
print("春风桃李花开夜")
class Tree:
def test(self):
print("对此如何不泪垂")
def leaf(self):
print("秋雨梧桐叶落时")
class Plant(Flower, Tree):
pass
plant = Plant()
# 继承所有类型的公共属性和方法
plant.open() # 春风桃李花开夜
plant.leaf() # 秋雨梧桐叶落时
# 方法重名按继承顺序查找
plant.test() # 芙蓉如面柳如眉
2.6 抽象类
3 多态
- 多态是程序运行过程中,根据运行时的数据和条件的差异,产生不同的处理状态,运行不同的逻辑代码的过程
- 多态没有固定的语法,有多种体现方式
- 特点:拓展性强、动态调用
class Plant:
def shape(self):
print("不同的东西的不同状态")
def desc(self):
self.shape()
class Hibiscus(Plant):
def shape(self):
print("芙蓉如面柳如眉")
class PeachBlossom(Plant):
def shape(self):
print("春风桃李花开夜")
class ChineseParasol(Plant):
def shape(self):
print("秋雨梧桐叶落时")
# 根据不同对象调用对象里的继承的同名方法,但结果不同(类似策略模式)
plant = Plant()
plant.desc() # 不同的东西的不同状态
flower = Hibiscus()
flower.desc() # 芙蓉如面柳如眉
flower2 = PeachBlossom()
flower2.desc() # 春风桃李花开夜
tree = ChineseParasol()
tree.desc() # 秋雨梧桐叶落时