在編寫Python代碼進(jìn)行自動(dòng)化測試画畅、網(wǎng)絡(luò)爬蟲或者其他與網(wǎng)絡(luò)相關(guān)的動(dòng)作的時(shí)候夜赵,由于網(wǎng)絡(luò)影響會(huì)容易失敗,而這種失敗并不是我們需要去處理的乡革。那么這種時(shí)候最好的辦法就是失敗后重試幾次寇僧,以避免網(wǎng)絡(luò)的間斷性影響摊腋。
如果我們正常編寫代碼的話,可能需要 try...except 嘁傀,但是這種寫法很麻煩兴蒸,能實(shí)現(xiàn)的效果也很單一。這里介紹一個(gè) Python 庫retrying
细办,專門用來對(duì)拋出異常的函數(shù)或者方法進(jìn)行重試橙凳。
通過 retrying
你能干什么:
- 出錯(cuò)后重新運(yùn)行函數(shù),直到正常運(yùn)行為止笑撞;
- 出錯(cuò)后岛啸,暫停一會(huì)再運(yùn)行函數(shù),因?yàn)榫W(wǎng)絡(luò)可能一時(shí)半會(huì)不會(huì)好茴肥;
- 出錯(cuò)后坚踩,如果重試時(shí)間過長,會(huì)造成代碼效率過低瓤狐,你可以設(shè)置一個(gè)最大的重試時(shí)間瞬铸;
- 出錯(cuò)時(shí),你可能想去執(zhí)行另一個(gè)函數(shù)以排除可能的錯(cuò)誤础锐。
那接下來我們看看用法嗓节,首先你需要安裝這個(gè)庫:
pip install retrying
簡單用法
簡單的示例,我們寫一個(gè)函數(shù)皆警,其中有一個(gè)變量 a 從 1 到 2 中隨機(jī)取一個(gè)拦宣, 當(dāng) a 為 1 的時(shí)候就拋出異常,我們嘗試在拋出異常時(shí)會(huì)不會(huì)重試:
import random
def demo():
a = random.randint(1,2)
print(a) # => 先打印耀怜,好看效果
if a == 1:
raise # 為1時(shí)拋出異常
這個(gè)函數(shù)有 50% 的概率會(huì)拋出錯(cuò)誤恢着,你可以先試試。不過如果你運(yùn)氣好的話财破。掰派。。
好左痢,接下來靡羡,我們加上 retrying :
from retrying import retry # => 引入retry裝飾器
import random
@retry() # => 裝飾你想重試的函數(shù),注意這里有括號(hào)
def demo():
a = random.randint(1,2)
print(a) # => 先打印俊性,好看效果
if a == 1:
raise # 為1時(shí)拋出異常
demo() # => 運(yùn)行這個(gè)可能報(bào)錯(cuò)的函數(shù)
運(yùn)行幾次看看略步,是不是當(dāng)打印 a 為 1 的時(shí)候會(huì)直到 a 為 2 為止?也就意味著@rery()
處理了異常并重試定页。
更多參數(shù)
也許你不滿足于讓它默認(rèn)重試趟薄,那么我們來看看更多的函數(shù)。
重試次數(shù)
stop_max_attempt_number
最大重試次數(shù)典徊,默認(rèn)為5次
設(shè)定一個(gè)最大重試次數(shù)杭煎,超過這個(gè)次數(shù)會(huì)停止重試恩够,并把異常拋出來。按照你的需要設(shè)置吧羡铲,一般不要太多就行∑嗣模或者你也可以不設(shè)雷恃,默認(rèn)為 5 次,不過前提時(shí)你修復(fù)了下面的bug押桃。
import random
from retrying import retry
@retry(stop_max_attempt_number=3)
def demo():
a = random.randint(1,2)
print(a, end=" ") # 為了排版导犹,打印橫排
if a != 3: # => 這里我們改為會(huì)始終拋異常的情況谎痢,不可能取到3
raise
demo()
## 1 1 1
## RuntimeError: No active exception to reraise
我們可以看到卷雕,運(yùn)行 3 次都報(bào)錯(cuò)后,第 3 次運(yùn)行的異常就拋出了滨嘱。
這里源代碼有個(gè) bug浸间,如果不設(shè)這個(gè)參數(shù)會(huì)無限重試(如果你的異常不會(huì)中止就是死循環(huán))。我們來修復(fù)這個(gè) bug ??:
打開 Python安裝目錄\Lib\site-packages\retrying.py
囊扳,找到第87
行
if stop_max_attempt_number is not None:
# 改為
if self._stop_max_attempt_number is not None:
時(shí)間相關(guān)
wait_fixed
重試的間隔時(shí)間
當(dāng)函數(shù)拋出異常后,下一次重試會(huì)間隔wait_fixed
設(shè)置的時(shí)間兜看。默認(rèn)是 1000 毫秒(1秒)锥咸,你可以通過這個(gè)參數(shù)修改這個(gè)默認(rèn)值细移。
wait_random_min
和 wait_random_max
用來設(shè)置隨機(jī)的間隔時(shí)間
wait_random_min 和 wait_random_max 搭配起來設(shè)置默認(rèn)的隨機(jī)等待時(shí)間,默認(rèn)是 0 ~ 1000 毫秒之間隨機(jī)等待雪侥。
wait_incrementing_increment
每重試一次,持續(xù)增加等待時(shí)間
默認(rèn)是 100 毫秒 亿扁,每重試一次,等待時(shí)常就會(huì)增加 100 毫秒从祝。
stop_max_delay
最長重試延遲時(shí)間牍陌,單位毫秒
這個(gè)不是重試間隔時(shí)間,這是函數(shù)運(yùn)行 + 重試結(jié)束的整體時(shí)間毒涧。比如stop_max_delay=2000
也就是說該函數(shù)從運(yùn)行到重試結(jié)束為止的時(shí)間為 2000 毫秒契讲,超過就結(jié)束重試并拋出異常滑频。如果你的函數(shù)運(yùn)行時(shí)間已經(jīng)超過 stop_max_delay 時(shí)間,就并不會(huì)重試峡迷。
import time
import random
from retrying import retry
@retry(stop_max_delay=2000) # => 設(shè)置了最大的重試時(shí)間
def demo():
a = random.randint(1, 2)
print(a, end=" ")
time.sleep(1) # => 這里等待了1秒
if a != 3:
raise
demo()
## 1 2
## RuntimeError: No active exception to reraise
關(guān)聯(lián)函數(shù)重試
retry_on_result
:指定一個(gè)函數(shù)绘搞,如果指定的函數(shù)返回True,則重試琉预,否則拋出異常退出楼雹。
import random
from retrying import retry
def fun():
# 處理代碼
return True # => 返回True
@retry(retry_on_result=fun) # 指定函數(shù),如果fun返回True則重試榨咐,否則不重試
def demo():
a = random.randint(1, 2)
print(a, end=" ")
if a != 3:
raise
demo()
shop_func
:指定被裝飾函數(shù)出錯(cuò)后谴供,會(huì)執(zhí)行的函數(shù),執(zhí)行該函數(shù)后在來重試被裝飾的函數(shù)数焊。注意,該函數(shù)必須要有兩個(gè)參數(shù)(attempts, delay)遂蛀。用來當(dāng)拋出異常后干厚,需要做一些處理的時(shí)候。
a = random.randint(1, 2)
def fun(attempts, delay):
global a
a = 3
print('待測函數(shù)')
@retry(stop_func=fun)
def demo():
print(a, end=" ")
time.sleep(1)
if a != 3:
raise
demo()