异常进阶写法
1、引言
小屌丝:鱼哥,最近python的文章写的有点少了!
小鱼:也不算少,只是最近大部分精力都放在全链路压测专栏的文章。
小屌丝:你这全链路压测没拦着你写,但是python文章也不能不更新啊。
小鱼:我凸(艹皿艹 )~ ~ 天天凌晨两点半才去睡觉了,还想我啥时候睡觉…
小屌丝:睡觉归睡觉,但是python文章不能不更新啊。
小鱼:我屮艸芔茻~ ~ 我白天上班,还得加加班,晚上10:30 ~ 凌晨2:30 才有时间写文章,你这是压榨! !
小屌丝:我这是为了妹子~ ~
小鱼:别那妹子做挡箭牌,快说吧,今天遇到啥问题了?
小屌丝:…说实话,最近有个妹子问我,异常有哪些写法!
小鱼:这个问题,问的很好。
关于异常的写法,小鱼最近在面试官的时候,也会经常问到求职者。
但是回答的,有点…不完整吧…
其实关于异常的写法,小鱼整理过一篇《常用断言及异常处理》,只是当时是在编写Web端自动化框架时,整理的,
那篇博文,更多的是python自带的库。
而今天我们要来介绍的,是第三方库–retrying!
2、异常进阶写法
2.1 普通写法
2.1.1 追加日志定位异常
我们在通过日志输出,来定位异常是很常见的一种,
例如:
# -*- coding: utf-8 -*-
# @ auth : carl_DJ
# @ time : 2021-11-19
def do_something():
pass
def log_error():
pass
try:
do_something()
except:
log_error()
这个写法,也是简单版的异常写法
2.1.2 增加重试机制
增加重试次数,也就是说第一次请求失败,再次发起请求,直到请求成功。
这个一般应用于两种场景:
- 爬虫
- 安全测试的攻防演练
我们来看下代码:
# -*- coding: utf-8 -*-
# @ auth : carl_DJ
# @ time : 2021-11-19
def do_something():
pass
attemps = 0
success = Flase
while attemps < 4 and not success:
try:
do_something()
success = True
except:
attemps += 1
if attemps ==4:
break
小屌丝:这个写法,不错呢。
小鱼:嗯,跟第一个相比,这个写法,更明确一下,也更高大上一些。
小屌丝:那还有更高大上的写法吗。
小鱼:那必须的,今天主角还没出场呢。
2.2 进阶写法
今天我们来说的这个模块,就是retrying,
也是第三方模块,主要是对程序中异常重试的一种优雅的解决方案。
2.2.1 安装
安装方式,老规矩,直接pip安装:
pip install retrying
如果觉得每次都pip install 费时费力,那就看看小鱼这两篇:
《Python3,选择Python自动安装第三方库,从此跟pip说拜拜!!》
《Python3,我低调的只用一行代码,就导入Python所有库!》
2.2.1 装饰器retry使用
retrying模块中,提供了一个装饰器函数retry;
装饰的函数会在运行失败的情况下重新执行,默认一直报错就一直重试,直到成功为止。
我们来看下代码
# -*- coding: utf-8 -*-
# @ auth : carl_DJ
# @ time : 2021-11-19
import random
from retrying import retry
#retry 装饰器
@retry
def do_something_retry():
if random.randint(0,10) > 1:
print(f'this is a test!')
raise IOError('raise exception!')
else:
return "Nice!"
print(do_something_retry())
小屌丝:为什么每次执行这段,都会打印出不同次数的"this is a test!"
小鱼:这是由于我们程序中只要随机整数大于1就会打印并且抛出异常。
但是由于我们有装饰器函数 retry,所以在发生异常就会重新再次执行方法,直到随机整数大于1,就会打印“Nice!”。
小屌丝:哦,这样啊,那我不想一直这么重试下去,能不能添加点限制之类的呢?
小鱼:嗯,可以的。我们不妨添加最大重试次数为4次。
2.2.2 添加最大次数限制
代码展示
# -*- coding: utf-8 -*-
# @ auth : carl_DJ
# @ time : 2021-11-19
import random
from retrying import retry
#retry 装饰器添加最大次数限制
@retry(stop_max_attempt_number = 4)
def do_something_times():
print(f"do something several times")
raise Exception("raise exception")
do_something_times()
小屌丝:真的呢,最多就是4次,那还有别的限制吗?
小鱼:这必须的。
2.2.3 添加最长重试时间
代码展示
# -*- coding: utf-8 -*-
# @ auth : carl_DJ
# @ time : 2021-11-19
from retrying import retry
# 限制最长重试时间(从执行方法开始计算)
@retry(stop_max_delay = 4000)
def do_something_intime():
print(f"do something in time")
raise Exception("raise exception")
do_something_intime()
2.2.4 设置固定重试时间
代码展示
# -*- coding: utf-8 -*-
# @ auth : carl_DJ
# @ time : 2021-11-19
from retrying import retry
# 设置固定重试时间
@retry(wait_fixed = 3000)
def wait_fixed_time():
print(f"wait")
raise Exception("raise exception")
wait_fixed_time()
2.2.4 设置时间重试范围
代码展示
# -*- coding: utf-8 -*-
# @ auth : carl_DJ
# @ time : 2021-11-19
from retrying import retry
# 设置重试时间的随机范围
@retry(wait_random_min=1000,wait_random_max=2000)
def wait_random_time():
print(f"wait")
raise Exception("raise exception")
wait_random_time()
2.2.5 设置特定异常类型
代码展示
# -*- coding: utf-8 -*-
# @ auth : carl_DJ
# @ time : 2021-11-19
from retrying import retry
#根据异常重试
def retry_if_io_error(exception):
return isinstance(exception, IOError)
# 设置特定异常类型重试
@retry(retry_on_exception=retry_if_io_error)
def retry_special_error():
print(f"retry io error")
raise IOError("raise exception")
retry_special_error()
在这里,我们自己定义一个函数,判断异常类型,然后将函数作为参数传给装饰函数 retry ,如果异常类型符合,就会进行重试。
2.2.6 根据返回值判断是否重试
代码展示
# -*- coding: utf-8 -*-
# @ auth : carl_DJ
# @ time : 2021-11-19
from retrying import retry
# 通过返回值判断是否重试
def retry_if_result_none(result):
# return result is None
if result =="123":
return True
@retry(retry_on_result=retry_if_result_none)
def might_return_none():
print("Retry forever ignoring Exceptions with no wait if return value is None")
return "123"
might_return_none()
这里我们定义了一个判断返回值的函数,然后将这个函数作为参数传给 retry 装饰函数。当结果返回是“123”时,就会一直重试执行 might_return_none 函数。
3、总结
在实际应用中,有的大佬偏向于使用第三方库,而有的大佬,喜欢自带的库。
不管是python自带的库,还是第三方库,在撸码过程中,能直接抛出异常,定位异常,就是最好。
所以,
- 如果喜欢自带库,那就看这篇:《常用断言及异常处理》
- 如果喜欢第三方库,就看此篇《Python3,异常进阶写法之retrying》