Python函数装饰器&异常处理
一,函数装饰器
1.1 函数装饰器应用场景
? 大家有没有听说过,开发和产品经理的故事。这两个物种凑合到一起,是火药味十足的。老有产品经理对开发说,卧槽!你这写得啥玩意儿??太low 了,你在执行前执行一个什么,执行以后再做一些什么操作。经常提需求,也经常改需求。对于我们开发来说,要是只有一个函数来说,我们改改还好,但是如果涉及到了100个函数,那这100个都需要改。
? 比如:支付功能涉及到100个函数,今天产品经理过来说,每个函数需要加一个验证用户登录得功能。那这100个函数,我们都得一个一个点开改!这他娘得不加班才怪呢!
def func1():
print("func1")
def func2():
print("func2")
def func3():
print("func3")
"""
下面有一万个函数
"""
func1()
func2()
func3()
? 现在产品经理过来拍了一下肩膀,“攀攀哇,我这有个简单得功能,你这么屌!一会儿就改完了。你把这一万个函数,都加一个验证用户功能呀”。卧槽,我当时心里一万只“草泥马,奔腾而过。加就加呗,我一个个点开函数加起来了,于是乎,经过我连续一个周得通宵加班,给他搞完了,如下:
def func1():
print("验证用户...")
print("func1")
def func2():
print("验证用户...")
print("func2")
def func3():
print("验证用户...")
print("func3")
"""
下面有一万个函数
"""
func1()
func2()
func3()
? 上线后,产品经理又走过来”攀攀哇,这个验证功能,我想了一下,用户体验度不好,咱还是给他删了吧!咦?你不会不想删吧?我今天日报刚好没什么跟老板汇报得,这个我可以汇报一下” 都这么说了,还能咋搞,硬着头皮改呗,于是乎连续一个周得通宵加班给删掉了。没过几天,产品经理又走过来,“攀攀哇,我想了一下,我修改了一下验证得逻辑,你再加上,这回一定行”,得!又得连续一个周得通宵加班~~当然连续三周通宵加班,我可能会猝死!!
? 当然,这个案例很夸张,大家不要慌。但是实际开发中,这种频繁修改需求是比较常见的。我们如何有效的解决这个问题,那么函数的装饰器,作用就体现出来了。
1.2 函数装饰器的写法
函数装饰器本质上是一个函数。
def outer(fun):
def wrapper():
print("支付之前:验证用户....")
fun()
print("支付之后: 谢谢老板....")
return wrapper
@outer
def func1():
print("执行:func1")
@outer
def func2():
print("执行:func2")
@outer
def func3():
print("执行:func3")
"""
下面有一万个函数
"""
func1()
func2()
func3()
? @outer这个注解,是让func1(),func2(),func3() 这些函数,与我们新写的outer()函数勾搭起来。在这里面outer()函数,就是func1,func2,func3 这些函数的装饰器。
1.3 当调用func1()函数之后执行步骤(debug模式看执行流程):
首先要注意,装饰器实际上是一个函数。
-
在需要调用的函数上面加上@装饰器函数名,比如@outer ,让装饰器和函数勾搭起来(建立连接) [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SbL1PNiY-1639397096253)(补充:函数装饰器&异常处理.assets/image-20211118155922983.png)] -
将调用的函数,绑定到装饰器函数的参数里面,比如上面的案例中,outer(fun) ,实际上fun = func1() [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CjcWTXgs-1639397096253)(补充:函数装饰器&异常处理.assets/image-20211118160013671.png)] -
加载装饰器函数中的内部函数wrapper()函数,并返回wrapper函数 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iEjInKKt-1639397096254)(补充:函数装饰器&异常处理.assets/image-20211118160113511.png)] -
此时原本调用的函数,会被返回的wrapper()函数替代。比如上面调用func1()最后会被修改成wrapper()函数 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FQ4lF4KG-1639397096254)(补充:函数装饰器&异常处理.assets/image-20211118160152619.png)] -
执行装饰器中的wrapper函数
?
1.4 带参数函数的装饰器
def outer(fun):
def wrapper(name):
print("支付之前:验证用户....")
fun(name)
print("支付之后: 谢谢老板....")
return wrapper
def func1(arg):
print("func1....",arg)
func1("攀哥...")
1.5 带返回值函数的装饰器
def outer(fun):
def wrapper(name):
print("支付之前:验证用户....")
result = fun(name)
print("支付之后: 谢谢老板....")
return result
return wrapper
def func1(name):
print("func1....", name)
return "%s真帅" % name
result = func1("攀哥...")
print(result)
1.6 作业:
1. 编写一个统计一个字符串中有多少个字符,和每一个字符出现多少次的函数
2. 为这个函数编写一个装饰器,用来计算执行这个函数需要多长时间
3. 提示: 获取系统当前的毫秒数,导入timer模块 然后使用:timer.timer()
?
二,异常处理
2.1 什么是异常
什么叫异常?超出逾期的。比如:
在开发过程中,切记,一定不要让用户看到系统出错的信息。
2.2 常见的异常
异常名称 | 描述 |
---|
| | BaseException | 所有异常的基类 | SystemExit | 解释器请求退出 | KeyboardInterrupt | 用户中断执行(通常是输入^C) | Exception | 常规错误的基类 | StopIteration | 迭代器没有更多的值 | GeneratorExit | 生成器(generator)发生异常来通知退出 | StandardError | 所有的内建标准异常的基类 | ArithmeticError | 所有数值计算错误的基类 | FloatingPointError | 浮点计算错误 | OverflowError | 数值运算超出最大限制 | ZeroDivisionError | 除(或取模)零 (所有数据类型) | AssertionError | 断言语句失败 | AttributeError | 对象没有这个属性 | EOFError | 没有内建输入,到达EOF 标记 | EnvironmentError | 操作系统错误的基类 | IOError | 输入/输出操作失败 | OSError | 操作系统错误 | WindowsError | 系统调用失败 | ImportError | 导入模块/对象失败 | LookupError | 无效数据查询的基类 | IndexError | 序列中没有此索引(index) | KeyError | 映射中没有这个键 | MemoryError | 内存溢出错误(对于Python 解释器不是致命的) | NameError | 未声明/初始化对象 (没有属性) | UnboundLocalError | 访问未初始化的本地变量 | ReferenceError | 弱引用(Weak reference)试图访问已经垃圾回收了的对象 | RuntimeError | 一般的运行时错误 | NotImplementedError | 尚未实现的方法 | SyntaxError | Python 语法错误 | IndentationError | 缩进错误 | TabError | Tab 和空格混用 | SystemError | 一般的解释器系统错误 | TypeError | 对类型无效的操作 | ValueError | 传入无效的参数 | UnicodeError | Unicode 相关的错误 | UnicodeDecodeError | Unicode 解码时的错误 | UnicodeEncodeError | Unicode 编码时错误 | UnicodeTranslateError | Unicode 转换时错误 | Warning | 警告的基类 | DeprecationWarning | 关于被弃用的特征的警告 | FutureWarning | 关于构造将来语义会有改变的警告 | OverflowWarning | 旧的关于自动提升为长整型(long)的警告 | PendingDeprecationWarning | 关于特性将会被废弃的警告 | RuntimeWarning | 可疑的运行时行为(runtime behavior)的警告 | SyntaxWarning | 可疑的语法的警告 | UserWarning | 用户代码生成的警告 |
2.3 异常的使用场景
比如,在除法运算的时候,我们知道除数不能为0.那么在程序中如果除数为0了,就会出现异常
num1 = int(input("请输入第一个数:"))
num2 = int(input("请输入第二个数:"))
print(num1/num2)
print(num1)
print(num2)
请输入第一个数:40
请输入第二个数:0
Traceback (most recent call last):
File "D:\py_work\study\basic\异常.py", line 4, in <module>
print(num1/num2)
ZeroDivisionError: division by zero
Process finished with exit code 1
? 我们会发现,当num2为0的时候,就会出现ZeroDivisionError: division by zero 异常信息。并且程序这个时候会终端,也就是后面的两行打印语句没有打印。
? 在开发中,为了用户的体验,避免用户看到爆红的错误信息,同时也为了系统的安全性,我们会屏蔽掉这个错误信息。这个时候我们需要捕捉并处理一下异常。
2.4 捕捉和处理异常的方式
捕捉异常可以使用 try/except语句。
try/except语句用来检测try语句块中的错误,从而让except语句捕获异常信息并处理。
如果你不想在异常发生时结束你的程序,只需在try里捕获它。
格式:
try:
<语句>
except <名字>:
<语句>
except <名字>,<数据>:
<语句>
else:
<语句>
看如下代码:
try:
num1 = int(input("请输入第一个数:"))
num2 = int(input("请输入第二个数:"))
print(num1/num2)
except ZeroDivisionError:
print("哥们儿!除数不为零?懂?")
else:
print("nice!我马上为你计算")
print(num1)
print(num2)
运行效果:
请输入第一个数:40
请输入第二个数:0
哥们儿!除数不为零?懂?
40
0
? 上面代码中,except ZeroDivisionError 表示捕捉除数为0的异常,如果num2=0,就会出现除数为0的异常,会被except捕捉到。然后执行except里面的代码。
? 如果num2不为0,就没有异常,这个时候可以执行else中的代码。
? 注意:else 不是必须的。可以不写!!
try 语句按照如下方式工作;
- 首先,执行 try 子句(在关键字 try 和关键字 except 之间的语句)。
- 如果没有异常发生,忽略 except 子句,try 子句执行后结束。
- 如果在执行 try 子句的过程中发生了异常,那么 try 子句余下的部分将被忽略。如果异常的类型和 except 之后的名称相符,那么对应的 except 子句将被执行。
- 如果一个异常没有与任何的 except 匹配,那么这个异常将会传递给上层的 try 中。
2.5 使用except而不带任何异常类型
try:
num1 = int(input("请输入第一个数:"))
num2 = int(input("请输入第二个数:"))
print(num1/num2)
except :
print("哥们儿!除数不为零?懂?")
else:
print("nice!我马上为你计算")
print(num1)
print(num2)
? 以上方式try-except语句捕获所有发生的异常。但这不是一个很好的方式,我们不能通过该程序识别出具体的异常信息。因为它捕获所有的异常。
2.6 如何同时捕获多个异常
格式1:
try:
....
except 异常1:
....
except 异常2:
....
else:
....
wife_list = ["赵丽颖","张天爱","宵鱼鱼","戴雨彤"]
try:
num1 = int(input("请输入第一个数:"))
num2 = int(input("请输入第二个数:"))
print(num1/num2)
for x in range(len(wife_list)):
print(x,".",wife_list[x])
index = int(input("请输入今晚要翻牌的编号"))
wife = wife_list[index]
print("你今天选的是:",wife)
except ZeroDivisionError:
print("哥们儿!除数不为零?懂?")
except IndexError:
print("哥们儿!醒醒你没有这个老婆")
else:
print("nice!我马上为你安排")
?
格式2:
try:
正常的操作
......................
except(Exception1[, Exception2[,...ExceptionN]]):
发生以上多个异常中的一个,执行这块代码
......................
else:
如果没有异常执行这块代码
例如:
wife_list = ["赵丽颖","张天爱","宵鱼鱼","戴雨彤"]
try:
num1 = int(input("请输入第一个数:"))
num2 = int(input("请输入第二个数:"))
print(num1/num2)
for x in range(len(wife_list)):
print(x,".",wife_list[x])
index = int(input("请输入今晚要翻牌的编号"))
wife = wife_list[index]
print("你今天选的是:",wife)
except (ZeroDivisionError,IndexError):
print("数据输入有误~~")
else:
print("这就给你安排")
? 当num2=0的时候,会发生除数为零的异常,会被except ZeroDivisionError: 捕获。如果选取了wife_list列表不存在的索引时,就会被except IndexError:捕获。如果没有发生异常,就会执行else中的语句
2.7 try-finally 语句
try-finally 语句无论是否发生异常都将执行最后的代码。
wife_list = ["赵丽颖","张天爱","宵鱼鱼","戴雨彤"]
try:
num1 = int(input("请输入第一个数:"))
num2 = int(input("请输入第二个数:"))
print(num1/num2)
for x in range(len(wife_list)):
print(x,".",wife_list[x])
index = int(input("请输入今晚要翻牌的编号"))
wife = wife_list[index]
print("你今天选的是:",wife)
except ZeroDivisionError:
print("哥们儿!除数不为零?懂?")
except IndexError:
print("哥们儿!醒醒你没有这个老婆")
else:
print("这就给你安排")
finally:
print("龙仔竭诚为你服务")
注意: else 和finally 不是必须得加得,根据实际情况而定
2.8 打印异常信息
一个异常可以带上参数,可作为输出的异常信息参数。
你可以通过except语句来捕获异常的参数,如下所示:
try:
正常的操作
......................
except ExceptionType as Argument:
你可以在这输出 Argument 的值...
比如:
while True:
try:
x = int(input("请输入一个数字: "))
break
except ValueError as e:
print(e)
print("您输入的不是数字,请再次尝试输入!")
2.9 抛出异常
Python 使用 raise 语句主动抛出一个指定的异常。
raise语法格式如下:
raise Exception("异常信息")
比如:我们一般在注册账号的时候要求密码必须是6-18位,如果用户输入错误,我们需要抛出异常
while True:
str = input("请输入6-18位密码")
if len(str)<6 or len(str)>18:
raise Exception("密码必须时6-18位")
|