文章目录
8. 面向对象特征 :封装、继承,多态
8.1 封装
8.1.1 封装概念
封装(Encapsulation)
: 在设计类时,刻意地将一些属性和方法隐藏在类的内部,这样在使用此类时,将无法直接以类对象.属性名
(或者类对象.方法名(参数)
)的形式调用这些属性(或方法),而只能用未隐藏的类方法间接操作这些隐藏的属性和方法- 封装作用
- 保证了
类内部数据结构的完整性
,避免外部对内部数据的影响,提高了程序的可维护性 避免
用户对类中属性或方法的不合理操作
- 提高代码的
复用性
- 保证了
8.1.2 封装方法
- 类中的变量和函数,可分为:
公有的
(类似 public 属性)和私有的
(类似 private)这 2 种属性 :公有属性
(public):类变量和类函数,在类的外部、类内部以及子类中都可以正常访问私有属性
(private):类变量和类函数,只能在本类内部使用,类的外部以及子类都无法使用
- python实现类的封装的方法:
- 默认情况下,Python 类中的变量和方法都是公有(public)的,它们的名称前都没有下划线
_
- 其名称以
双下划线
__
开头的变量(函数)为私有变量(私有函数),其属性等同于 private - 以
单下划线 _
开头的类属性或者类方法,通常被视为私有属性
和私有方法
- 默认情况下,Python 类中的变量和方法都是公有(public)的,它们的名称前都没有下划线
注意 :Python 类中还有以双下划线开头和结尾的类方法(例如类的构造函数__init__(self)),这些都是 Python 内部定义的,用于 Python 内部调用,自己定义类属性或者类方法时,不要使用这种格式。
- 封装底层实现原理
- 以双下划线开头命名的类属性或类方法,其底层实现封装的原理就是,将它们的名称都偷偷改成了
_类名__属性(方法)名
的格式 - 因此使用
对象名._类名__属性(方法)名
就可以调用出封装的属性或者方法
8.2 继承
8.2.1 继承基本概念
- 基本概念
继承机制
:用于创建和现有类功能类似的新类,创建的新类就只需要在现有类基础上添加一些成员(属性和方法),实现类的重复使用
- 子类 :实现继承的类
- 父类 :被继承的类(也可称为基类、超类)
- 继承 :继承是相对子类来说的,即
子类继承自父类
- 派生 :派生是相对于父类来说的,即
父类派生出子类
- 语法格式
class 类名(父类1, 父类2, ...):
#类定义部分
- 特点
多继承机制
:一个子类可以同时拥有多个直接父类- 如果类没有显式指定继承自哪个类,则默认继承
object
类(object 类是 Python 中所有类的父类) 子类拥有父类所有的属性和方法
,包括私有(private)的属性或方法- 当父类中有同名的属性和方法时,子类根据父类的
前后次序决定继承
那个,即排在前面父类中的类方法会覆盖排在后面父类中的同名类方法(自己编写程序时,尽量不要使用多继承)
8.2.2 多继承机制的继承顺序
- 继承顺序可分为
深度优先
和广度优先
两种机制 - 如下继承关系 :
- 深度优先的继承顺序 :D - B - A - C
- 广度优先的继承顺序 :D - B - C - A
8.2.3 父类方法重写
- 概念
父类方法重写
:继承的父类方法不适用子类时,需要重写该方法- 子类重写的方法名和父类一样,根据都继承搜索原则,会先调用子类中的方法
- 重写方法
class A( ):
def fun(self):
pass
class B(A):
def fun(self):
pass
cl = B()
cl.fun() # 调用B类中方法fun()
# 假如需要使用A类中的方法fun(),手动传入self值
A.fun(cl)
8.2.4 super() 调用父类的构造方法
- 子类中的构造方法中,调用父类构造方法的方式有 2 种 :
- 类可以看做一个独立空间,在类的外部调用其中的实例方法,可以向调用普通函数那样,只不过需要额外备注类名(此方式又称为
未绑定方法
) - 使用
super()
函数,在多继承,该函数只能调用第一个直接父类
的构造方法,语法格式 :super().__init__(parm...)
- 类可以看做一个独立空间,在类的外部调用其中的实例方法,可以向调用普通函数那样,只不过需要额外备注类名(此方式又称为
class a() :
def __init__(self, name) :
self.name = name
pass
class b():
def __init__(self, value) :
self.value = value
pass
class c(a, b) :
def __init__(self, name, value):
super().__init__(name) # super()方法
b.__init__(self, value) # 未绑定方法
8.2.4 类或实例对象添加方法
- 动态添加方法规则
- 类添加方法 :python 允许为类动态地添加:实例方法、静态方法和类方法
- 实例对象添加方法 : 只允许动态地添加实例方法,不能添加类方法和静态方法
- 单个实例对象添加方法,不会影响该类的其它实例对象
- 为类动态地添加方法,则所有的实例对象都可以使用
- 方法实例
class CLanguage:
pass
def info(self): # 定义了一个实例方法
pass
@classmethod
def info2(cls): # 定义了一个类方法
pass
@staticmethod
def info3(): # 定义个静态方法
pass
# 类可以动态添加以上 3 种方法,会影响所有实例对象
CLanguage.info = info
CLanguage.info2 = info2
CLanguage.info3 = info3
clang = CLanguage() # 定义类的实例对象,clang 具有以上 3 种方法
#类实例对象只能动态添加实例方法,不会影响其它实例对象
clang1 = CLanguage()
clang1.info = info
clang1.info(clang1) # 调用必须手动为 self 传值
__slots__
作用即方法- 限制为
实例对象
动态添加属性和方法法,无法限制动态地为类添加属性和方法 __slots__
属性值是一个元组,只有其中指定的元素,才可以作为动态添加的属性或者方法的名称__slots__
限制的是其方法名,并不限制参数的个数__slots__
属性对由该类派生出来的子类,也是不起作用的- 如下, 以
A
创建的实例对象,只能动态添加 name、add、info 这 3 个属性以及 name()、add() 和 info() 这 3 个方法
- 限制为
class A():
__slots__ = ('name','add','info')
pass
8.2.5 动态创建类
- type() 函数
type(obj)
: 查看某个变量(类对象)的具体类型type(name, bases, dict)
: 用来创建类- name 表示
类的名称
- bases 表示一个元组,其中存储的是该
类的父类
- dict 表示一个字典,用于表示类内定义的
属性
或者方法
- name 表示
8.3 多态
- 含义
- 用一个函数名实现调用不同类中的不同内容(功能)的函数方法
- 特点
- 只关心对象的实例方法是否同名,不关心对象所属的类型
- 对象所属的类之间,继承关系可有可无
- 增加代码的外部调用灵活度,让代码更加通用,兼容性比较强
- 多态是调用方法的技巧,不会影响到类的内部设计
- 使用示例
class CLanguage:
def say(self):
print("调用的是 Clanguage 类的say方法")
class CPython(CLanguage):
def say(self):
print("调用的是 CPython 类的say方法")
class CLinux(CLanguage):
def say(self):
print("调用的是 CLinux 类的say方法")
class WhoSay:
def say(self, who):
who.say()
a = WhoSay()
a.say(CLanguage()) # 调用 CLanguage 类的 say() 方法
a.say(CPython()) # 调用 CPython 类的 say() 方法
a.say(CLinux()) # 调用 CLinux 类的 say() 方法
感谢阅读 若有错误 敬请见谅 !!!