Python单例与异常

学习地址

单例

目标

  1. 单例设计模式
  2. __new__方法
  3. Python中的单例

单例设计模式

  1. 设计模式

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

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

    • 音乐播放对象
    • 回收站对象
    • 打印机对象
    • 都只有唯一的一个存在

__new__方法

使用类名()创建对象时,Python解释器会调用__new__方法为对象分配空间 __new__是一个由object基类提供的内置的静态方法,主要作用有两个

  • 在内存中为对象分配空间
  • 返回对象的引用

Python的解释器获得对象的引用后,将引用作为第一个参数,传递给__init__方法

了解__new__方法就可以进行改造了
重写__new__方法的代码十分固定!

重写__new__方法一定要return super().__new__(cls)
否则,Python解释器得不到分配了空间的对象引用,就不会调用对象的初始化方法
注意:__new__是一个静态方法,在调用时需要主动传递参数cls
示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class MusicPlayer(object):
def __new__(cls, *args, **kwargs):
# auto call
print('Create objects, allocate memory')
       # 为对象分配空间并返回引用
       # return super().__new__(cls)

def __init__(self):
print('player init....')

player=MusicPlayer()
print(player)

- - - - - - -
Create objects, allocate memory
None

Python中的单例

单例

让类创建的对象,在系统中只有唯一的一个实例,即每次创建对象所返回的引用是一样的

  1. 定义一个类属性,初始值是None,用于记录单例对象的引用
  2. 重写__new__方法
  3. 如果类属性is None,调用父类方法分配空间,记录引用
  4. 否则,返回这个类属性代表的引用
实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class MusicPlayer(object):
instance=None
def __new__(cls, *args, **kwargs):
# auto call
# print('Create objects, allocate memory')
if MusicPlayer.instance is None: MusicPlayer.instance=super().__new__(cls)
return MusicPlayer.instance

def __init__(self):
print('player init....')

player1=MusicPlayer()
print(player1)
player2=MusicPlayer()
print(player2)

- - - - - - - - - - - -
player init....
<__main__.MusicPlayer object at 0x7f2f98dd69b0>
player init....
<__main__.MusicPlayer object at 0x7f2f98dd69b0>

可以看到两次创建对象的内存地址是一样的

代码升级

需求

  • 使得单例对象在被创建时,只执行一次初始化工作(音乐播放器换歌不需要重启)

解决

  • 定义一个类属性init_flag初始值为False标记是否执行过初始化动作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class MusicPlayer(object):
instance=None
init_flag=False
def __new__(cls, *args, **kwargs):
# auto call
# print('Create objects, allocate memory')
if MusicPlayer.instance is None: MusicPlayer.instance=super().__new__(cls)
return MusicPlayer.instance

def __init__(self):
if MusicPlayer.init_flag == True:return
print('player init....')
MusicPlayer.init_flag=True

player1=MusicPlayer()
print(player1)
player2=MusicPlayer()
print(player2)

- - - - - - - - - - - -
player init....
<__main__.MusicPlayer object at 0x7f5e4abc4a20>
<__main__.MusicPlayer object at 0x7f5e4abc4a20>

异常

目标

  1. 概念
  2. 捕获异常
  3. 异常的传递
  4. 自定义异常

概念

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

异常捕获

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

简单的捕获异常语法

在程序开发中,如果对某些代码的执行不能确定是否正确,可以增加try:来捕获异常

1
2
3
4
5
6
try:
# try to do
pass
except:
# deal with error
pass

比如程序设计中常见的多组输入

1
2
3
4
5
while True:
try:
a,b=int(input())
print(a+b)
except:break

根据错误类型捕获异常

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

1
2
3
4
5
6
7
8
9
10
try:
   # 尝试执行的代码
pass
except error_1:
   # 针对错误类型1,对应的代码处理,下面同理
   pass
except (error_2,error_3):
pass
except Exception as result: # 捕获未知错误
   print('unknown mistake: %s' %result)

当Python解释器抛出异常时,最后一行错误信息的第一个单词就是错误类型,即代码中的error,如ValueErrorZeroDivisionError等等

异常捕获完整语法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
try:
   # 尝试执行
   pass
except error_1:
   # 错误类型1,对应的处理
   pass
except (error_2,error_3):
pass
except Exception as result:
print('unknown mistake: %s' result)
else:
   # 没有异常会执行的代码
   pass
finally:
   # 无论是否有异常,都会执行的代码
   print("this must be execution")

异常的传递性

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

提示:

  1. 在开发中,可以在主函数中增加异常捕获
  2. 主函数中调用的其他函数,出现异常都会传递到主函数的异常捕获中
  3. 只需在主函数中进行异常捕获,保证代码整洁性

主动抛出异常

在开发中,除了代码执行出错Python解释器会抛出异常外
还可以根据应用程序特有的需求业务主动抛出异常

抛出异常

Python中提供了一个Exception异常类
在开发时,如果满足特定业务需求时,希望抛出异常,可以:

  1. 创建一个Exception对象
  2. 使用raise关键字抛出异常对象
示例

需求

  1. 定义input_password函数,提示用户输入密码
  2. 如果用户输入长度<8,抛出异常
  3. 如果用户输入长度>=8,返回输入的密码

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def input_password():
password=input('please input password: ')
if len(password)>=8:return password

print('raise an exception')
exp=Exception('the length of password is not enough')
raise exp

try:
print(input_password())
except Exception as result:
print(result)

- - - - - - - - - - - - -
please input password: 12345
raise an exception
the length of password is not enough