python面向对象(六、单例、异常、约束)

7.单例
7.1单例设计模式
1)设计模式
设计模式是前人工作的总结和提炼,通常被人们广泛流传的设计模式都是针对某一特定问题的成熟的解决方案
使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性

2)单例设计模式
目的:让类创建的对象,在系统中只有 唯一的一个实例
每一次执行 类名() 返回的对象,内存地址是相同的

3)单例设计模式的应用场景
音乐播放 对象
回收站 对象
打印机 对象
……

7.2 __new__方法(重写)
使用类名()创建对象时,Python的解释器首先会调用__new__方法为对象分配空间
__new__是一个由object基类提供的内置的静态方法,主要作用有两个:
(1)在内存中为对象分配空间
(2)返回对象的引用

Python的解释器获得对象的引用后,将引用作为第一个参数,传递给__init__方法
重写__new__方法的代码非常固定!
重写__new__方法一定要return super().new(cls),否则 Python 的解释器得不到分配了空间的对象引用,就不会调用对象的初始化方法
注意:__new__是一个静态方法,在调用时需要主动传递cls参数

7.3 Python 中的单例
单例:让 类 创建的对象,在系统中只有 唯一的一个实例
(1)定义一个类属性,初始值是None,用于记录 单例对象的引用
(2)重写__new__方法
(3)如果类属性is None,调用父类方法分配空间,并在类属性中记录结果
(4)返回类属性中记录的对象引用

7.4 只执行一次初始化工作
在每次使用“类名()”创建对象时,Python的解释器都会自动调用两个方法:
(1)__new__分配空间
(2)__init__对象初始化
在上一小节对__new__方法改造之后,每次都会得到第一次被创建对象的引用
但是:初始化方法还会被再次调用

需求:让初始化动作只被执行一次
解决办法:
(1)定义一个类属性“init_flag”标记是否执行过初始化动作,初始值为False
(2)在__init__方法中,判断init_flag,如果为False就执行初始化动作
(3)然后将init_flag设置为True
(4)这样,再次自动调用__init__方法时,初始化动作就不会被再次执行了


结果:虽然创建了多个对象,但只执行1次 print("初始化音乐播放器")
简化版(省略初始化标识):

注意:高并发的情况下,单例是有问题的。高并发时,多个线程跑到new方法,发现MusicPlayer.__instance是空,会同时创建多个MusicPlayer.__instance并返回。需要使用队列处理高并发才能解决该问题。

8.异常
8.1异常的概念
程序在运行时,如果Python解释器遇到到一个错误,会停止程序的执行,并且提示一些错误信息,这就是异常
程序停止执行并且提示错误信息这个动作,我们通常称之为:抛出(raise)异常

正常运行:

异常:

程序开发时,很难将所有的特殊情况都处理的面面俱到,通过异常捕获可以针对突发事件做集中的处理,从而保证程序的稳定性和健壮性

8.2 捕获异常
8.2.1 简单的捕获异常语法
在程序开发中,如果对某些代码的执行不能确定是否正确,可以增加try(尝试)来捕获异常
捕获异常最简单的语法格式:

try尝试,下方编写要尝试代码,不确定是否能够正常执行的代码
except如果不能正常执行,下方编写尝试失败的代码
常见错误:

简单异常捕获演练:要求用户输入整数

8.2.2 错误类型捕获
在程序执行时,可能会遇到不同类型的异常,并且需要针对不同类型的异常,做出不同的响应,这个时候,就需要捕获错误类型了
1)语法如下:

当Python解释器抛出异常时,最后一行错误信息的第一个单词,就是错误类型

2)异常类型捕获演练——要求用户输入整数
(1)提示用户输入一个整数
(2)使用8除以用户输入的整数并且输出

捕捉多种错误类型:

3)捕获未知错误
在开发时,要预判到所有可能出现的错误,还是有一定难度的
如果希望程序无论出现任何错误,都不会因为Python解释器抛出异常而被终止,可以再增加一个except,语法如下:


不返回错误类型

8.2.3 异常捕获完整语法
在实际开发中,为了能够处理复杂的异常情况,完整的异常语法如下:

else只有在没有异常时才会执行的代码
finally无论是否有异常,都会执行的代码
之前一个演练的完整捕获异常的代码如下:

8.2.4 traceback查看堆栈信息
如果异常被捕获为未知错误,该方法可以返回错误信息,主要用于调试使用

8.3 异常的传递
异常的传递:当函数/方法执行出现异常,会将异常传递给函数/方法的调用一方。如果传递到主程序仍然没有异常处理,程序才会被终止

1)提示:
(1)在开发中,可以在主函数中增加异常捕获
(2)而在主函数中调用的其他函数,只要出现异常,都会传递到主函数的异常捕获中
(3)这样就不需要在代码中,增加大量的异常捕获,能够保证代码的整洁

2)需求:
(1)定义函数demo1()提示用户输入一个整数并且返回
(2)定义函数demo2()调用demo1()
(3)在主程序中调用demo2()

主程序成功捕获异常

8.4 抛出raise异常
8.4.1 应用场景
在开发中,除了代码执行出错Python解释器会抛出异常之外
还可以根据应用程序特有的业务需求主动抛出异常
示例:
提示用户输入密码,如果长度少于8,抛出异常

注意:
当前函数只负责提示用户输入密码,如果密码长度不正确,需要其他的函数进行额外处理
因此可以抛出异常,由其他需要处理的函数捕获异常

8.4.2 抛出异常
1)直接抛出异常

2)使用Exception抛出异常
Python中提供了一个Exception异常类
在开发时,如果满足特定业务需求时,希望抛出异常,可以:
(1)创建一个Exception的对象(创建一个异常)
(2)使用raise关键字抛出异常对象
需求:
(1)定义input_password函数,提示用户输入密码
(2)如果用户输入长度<8,抛出异常
(3)如果用户输入长度>=8,返回输入的密码

抛出异常后,我们还可以捕获异常

3)创建异常类

9.约束
父类可以对子类进行约束,要求子类必须重写父类对象。便于使用中统一调用
1)raise NotImplementedError: 抛出异常,表示子类必须重写这个方法

2)抽象类:没有具体实例
from abc import ABCMeta, abstractmethod # ABCMeta:原类,abstractmethod:抽象方法装饰器
class 类(metaclass=ABCMeta)
@abstractmethod
def func(self):pass

posted on 2020-12-02 14:02  叮当2000  阅读(135)  评论(0编辑  收藏  举报