01.16 单例、异常、eval函数

一、单例

01. 单例设计模式

设计模式 设计模式前人工作的总结和提炼,通常,被人们广泛流传的设计模式都是针对 某一特定问题 的成熟的解决方案 使用 设计模式 是为了可重用代码、让代码更容易被他人理解、保证代码可靠性单例设计模式 目的 —— 让 创建的对象,在系统中 只有 唯一的一个实例 每一次执行 类名() 返回的对象,内存地址是相同的

单例设计模式的应用场景

音乐播放 对象回收站 对象打印机 对象……

02. __new__ 方法

使用 类名() 创建对象时, Python 的解释器 首先 会 调用 __new__ 方法为对象 分配空间__new__ 是一个 由 object 基类提供的 内置的静态方法,主要作用有两个: 1) 在内存中为对象 分配空间 2) 返回 对象的引用Python 的解释器获得对象的 引用 后,将引用作为 第一个参数,传递给 __init__ 方法

重写 __new__ 方法 的代码非常固定!

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

<code> class MusicPlayer(object):



def __new__(cls, *args, **kwargs):
# 如果不返回任何结果,
return super().__new__(cls)

def __init__(self):
print("音乐播放器初始化")


yunplayer = MusicPlayer()
print(yunplayer)/<code>

03. Python 中的单例

单例 —— 让 类 创建的对象,在系统中 只有 唯一的一个实例

定义一个 类属性,初始值是 None,用于记录 单例对象的引用重写 __new__ 方法如果 类属性 is None,调用父类方法分配空间,并在类属性中记录结果返回 类属性 中记录的 对象引用

<code> class MusicPlayer(object):

# 定义类属性记录单例对象引用
instance = None


def __new__(cls, *args, **kwargs):
if cls.instance is None:
return super().__new__(cls)

return cls.instance/<code>

只执行一次初始化工作

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

需求

初始化动作 只被 执行一次

解决办法

定义一个类属性 init_flag 标记是否 执行过初始化动作,初始值为 False在 __init__ 方法中,判断 init_flag,如果为 False 就执行初始化动作然后将 __init__ 设置为 True这样,再次 自动 调用 __init__ 方法时,初始化动作就不会被再次执行

<code> class MusicPlayer(object):



# 记录第一个被创建对象的引用
instance = None
# 记录是否执行过初始化动作
init_flag = False

def __new__(cls, *args, **kwargs):
# 1.判断类属性是否为空对象
if cls.instance is None:
# 2.调用父类方法,为第一个对象分配空间
return super().__new__(cls)
# 3.返回类属性保存的对象引用
return cls.instance

def __init__(self):
if not MusicPlayer.init_flag:
print("音乐播放器初始化")

MusicPlayer.init_flag = True


# 创建对个对象
player1 = MusicPlayer()
print(player1)
player2 = MusicPlayer()
print(player2)/<code>

二、异常

01. 异常的概念

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

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

02. 捕获异常

2.1 简单的捕获异常语法

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

<code>try:
尝试执行的代码
except:
出现错误的处理/<code>try 尝试,下方编写要尝试代码,不确定是否能够正常执行的代码except 如果不是,下方编写尝试失败的代码

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

<code>try:
# 提示用户输入一个数字
num = int(input("请输入数字:"))
except:
print("请输入正确的数字")/<code>

2.2 错误类型捕获

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

<code>try:
# 尝试执行的代码
pass
except 错误类型1:
# 针对错误类型1,对应的代码处理
pass
except (错误类型2, 错误类型3):
# 针对错误类型2 和 3,对应的代码处理
pass
except Exception as result:
print("未知错误 %s" % result)/<code>当 Python 解释器 抛出异常 时,最后一行错误信息的第一个单词,就是错误类型

异常类型捕获演练 —— 要求用户输入整数

需求

提示用户输入一个整数使用 8 除以用户输入的整数并且输出

<code>try:
num = int(input("请输入整数:"))
result = 8 / num
print(result)
except ValueError:


print("请输入正确的整数")
except ZeroDivisionError:
print("除 0 错误")/<code>

捕获未知错误

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

语法如下:

<code>except Exception as result:
print("未知错误 %s" % result)/<code>

2.3 异常捕获完整语法

在实际开发中,为了能够处理复杂的异常情况,完整的异常语法如下: 提示: 有关完整语法的应用场景,在后续学习中,结合实际的案例会更好理解现在先对这个语法结构有个印象即可

<code>try:
# 尝试执行的代码
pass
except 错误类型1:
# 针对错误类型1,对应的代码处理
pass
except 错误类型2:
# 针对错误类型2,对应的代码处理


pass
except (错误类型3, 错误类型4):
# 针对错误类型3 和 4,对应的代码处理
pass
except Exception as result:
# 打印错误信息
print(result)
else:
# 没有异常才会执行的代码
pass
finally:
# 无论是否有异常,都会执行的代码
print("无论是否有异常,都会执行的代码")/<code>else 只有在没有异常时才会执行的代码finally 无论是否有异常,都会执行的代码之前一个演练的 完整捕获异常 的代码如下:

<code>try:
num = int(input("请输入整数:"))
result = 8 / num
print(result)
except ValueError:
print("请输入正确的整数")
except ZeroDivisionError:
print("除 0 错误")
except Exception as result:
print("未知错误 %s" % result)
else:
print("正常执行")
finally:
print("执行完成,但是不保证正确")/<code>

03. 异常的传递

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

  提示

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

需求

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

<code>def demo1():
return int(input("请输入一个整数:"))


def demo2():
return demo1()



try:
print(demo2())
except ValueError:
print("请输入正确的整数")
except Exception as result:
print("未知错误 %s" % result)/<code>

04. 抛出 raise 异常

4.1 应用场景

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

示例

提示用户 输入密码,如果 长度少于 8,抛出 异常

注意

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

4.2 抛出异常

Python 中提供了一个 Exception 异常类在开发时,如果满足 特定业务需求时,希望 抛出异常,可以: 创建 一个 Exception 的 对象 使用 raise 关键字 抛出 异常对象

需求

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

<code>def input_password():

# 1. 提示用户输入密码
pwd = input("请输入密码:")

# 2. 判断密码长度,如果长度 >= 8,返回用户输入的密码
if len(pwd) >= 8:
return pwd

# 3. 密码长度不够,需要抛出异常
# 1> 创建异常对象 - 使用异常的错误信息字符串作为参数
ex = Exception("密码长度不够")

# 2> 抛出异常对象
raise ex


try:
user_pwd = input_password()
print(user_pwd)
except Exception as result:
print("发现错误:%s" % result)/<code>

三、eval 函数

eval() 函数十分强大 —— 将字符串 当成 有效的表达式 来求值 并 返回计算结果

<code># 基本的数学计算
In [1]: eval("1 + 1")
Out[1]: 2

# 字符串重复
In [2]: eval("'*' * 10")
Out[2]: '**********'

# 将字符串转换成列表


In [3]: type(eval("[1, 2, 3, 4, 5]"))
Out[3]: list

# 将字符串转换成字典
In [4]: type(eval("{'name': 'xiaoming', 'age': 18}"))
Out[4]: dict/<code>

案例 - 计算器

需求

提示用户输入一个 加减乘除混合运算返回计算结果

<code>input_str = input("请输入一个算术题:")

print(eval(input_str))/<code>

不要滥用 eval

在开发时千万不要使用 eval 直接转换 input 的结果

<code>__import__('os').system('ls')/<code>等价代码

<code>import os

os.system("终端命令")/<code>执行成功,返回 0执行失败,返回错误信息