面向对向-补充
一、面向对象的三大特征
- 封装
- 将事物的属性和方法写入到类中
- 可以定义私有属性和方法
- 实例空间保存实例属性,其余的属性和所有方法保存在类空间
- 继承
- 每个类都有至少一个父类,子类继承父类后,那么子类就拥有父类中所有的属性和方法
- 子类不能继承私有属性和方法
- 多态
- 在方法中传入不同的对象,会产生不同的结果
二、多态
2.1 多态的概念
多态指的是事物的多种形态。
多态的前提是继承。
步骤:
- 定义一个父类,并且创建父类中相应的公共方法
- 定义多个子类,重写这个公共方法
- 将子类的实例传入到某个对象中,产生不同的形态(结果)
2.2 代码实现
背景:
UNIX是最早的通用操作系统,通用表示系统的可移植性高
类UNIX:根据UNIX操作系统的开发规范开发的仿制品,比如:
Linux
、macOS(FreeBSD)
# 定义一个UNIX类
class MyUnix(object):
def work(self):
print('UNIX可以玩游戏')
# 定义一个Linux类
class MyLinux(MyUnix):
def work(self):
print('Linux可以作为服务器')
# 定义一个macOS类
class MyMac(MyUnix):
def work(self):
print('mac可以做设计和编程')
# 定义一个用户类
class User(object):
def use_os_work(self, os_obj):
os_obj.work()
# 实例化两个操作系统子类
linux1 = MyLinux()
mac1 = MyMac()
# 实例化两个用户
user1 = User()
user2 = User()
# 让用户使用两个操作系统
user1.user_os_work(linux1) # Linux可以作为服务器
User.use_os_work(user2, mac1) # mac可以做设计和编程
三、类属性和实例属性
类属性是定义在类中并且在方法外的属性,它保存在类空间中;
一般类属性定义为变量,直接指向一个对象
3.1 类属性
3.1.1 定义和访问类属性
类属性保存在类空间,类属性被类对象和实例对象访问
class MyLinux(object):
# 定义类属性,属性表示Linux的内核版本
kernel_version = 5
# 定义实例方法,初始化不同的Linux发行版
def __init__(self, distro):
self.distro = distro
# 通过类对象来访问
print(MyLinux.kernel_version)
# 实例化两个不同的Linux发行版
centos8 = MyLinux('CentOS8')
ubuntu20 = MyLinux('Ubuntu20')
# 通过实例对象来访问类属性
print(centos8.kernel_version) # 5
print(ubuntu20.kernel_version) # 5
定义类属性,只在类空间创建,如果定义实例属性,会在多个实例空间创建,缺点是占用内存资源
3.1.2 修改类属性
类属性只能通过类对象修改;
实例对象修改类属性,相当于创建一个实例属性
class MyLinux(object):
kernel_version = 5
# 1. 通过类对象修改类属性,可以修改
MyLinux.kernel_version = 3
print(MyLinux.kernel_version) # 3
# 实例化两个对象
centos8 = MyLinux()
ubuntu20 = MyLinux()
# 2.通过实例对象修改类属性
centos8.kernel_version = 4 # 相当于创建一个实例属性
print(centos8.kernel_version) # 4。
print(ubuntu20.kernel_version) # 3
- 实例对象不能修改类属性;
- 实例对象访问属性时,先在自身空间中查找,如果有则访问成功,否则访问类空间,类空间也没有则报错。
3.2 实例属性
实例属性保存在实例空间;
类对象不能访问实例属性
class MyLinux(object):
def __init__(self):
self.distro = 20
ubuntu1 = MyLinux()
# 1.1 通过实例对象访问实例属性
print(ubuntu1.distro) # 20
# 1.2 通过类对象访问实例属性
print(MyLinux.distro) # 报错
# 2.1 实例对象修改实例属性
ubuntu1.distro = 18
print(ubuntu1.distro) # 18
实例属性只能通过实例对象访问;
多个实例对象不能相互访问
四、类方法和静态方法
4.1 类方法
- 类方法的第一个形参必须为类对象的引用,一般使用
cls
- 类方法必须使用装饰器
@classmethod
来修饰类方法
class MyLinux(object):
kernel = 5
# 定义类方法
@classmethod
def get_kernel(cls):
print(f'版本为{cls.kernel}') # 在类方法中访问类属性
print(f'{MyLinux.kernel}')
# 定义实例方法
def get_kernel_instance(self):
print(f'版本为{self.kernel}') # 在实例方法中使用 self.类属性 可以访问成功
# 实例化一个对象
ubuntu1 = MyLinux()
# 对象调用实例方法,OK
ubuntu1.get_kernel_instance() # 版本为5
# 实例对象调用类方法,OK
ubuntu1.get_kernel() # 可以在类的外部,可以通过实例对象访问类方法
# # 类对象调用类方法,OK
MyLinux.get_kernel() # 版本为5
类方法的使用:
一般要在方法中操作类属性,那么就定义一个类方法;
4.2 静态方法
静态方法一般不传入
cls
或者self
;静态方法需要使用
@staticmethod
来修饰
class MyLinux(object):
# 定义静态方法,打印我是Linux发行的说明书
@staticmethod
def get_info():
print('Linux发行的说明书')
@classmethod
def get_static(cls):
cls.get_info()
def get_static_instance(self):
self.get_info()
# 1. 通过类对象调用
MyLinux.get_info() # Linux发行的说明书
MyLinux.get_static() # Linux发行的说明书
# 2. 通过实例对象来调用
centos1 = MyLinux()
centos1.get_info() # Linux发行的说明书
centos1.get_static_instance() # Linux发行的说明书
静态方法可以使用类对象或者实例对象来调用;
在类的内部,可以使用
cls
或者self
来调用静态方法静态方法一般用来作为实例对象的公共方法。
五、单例设计模式
无论实例化多少次,只创建一个内存空间
5.1 对象的实例过程
__new__
:为实例对象创建一个内存空间,并将内存地址返回给CPython
解释器__init__
:解释器在已创建的内存空间中添加实例属性
5.2 单例设计模式的原理
- 重写
__new__
方法,如果没有创建内存空间则创建;如果已经创建空间,则不创建; - 定义
__init__
方法,如果没有初始化则进行初始化;如果已经初始化,则不初始化。
5.3 代码实现
class Student(object):
# 创建一个类属性,表示内存的空间
mem_space = None # 假设没有创建实例空间,None表示没有实例空间
ins_ = False # 没有进行属性的添加
def __new__(cls, *args, **kwargs):
if not cls.mem_space: # 如果没有创建实例空间,则创建
cls.mem_space = super().__new__(cls)
return cls.mem_space
def __init__(self):
if not Student.ins_:
# 如果没有初始化实例,则初始化,并将类属性修改为True
Student.ins_ = True
print('这个是init方法')
s1 = Student()
s2 = Student()
s3 = Student()
print(s1)
print(s2)
print(s3)
''' 输出结果:
这个是init方法
<__main__.Student object at 0x000001D7E0F47310>
<__main__.Student object at 0x000001D7E0F47310>
<__main__.Student object at 0x000001D7E0F47310>
'''