Python出錯重試:retrying

在編寫Python代碼進行自動化測試、網絡爬蟲或者其他與網絡相關的動作的時候,由於網絡影響會容易失敗,而這種失敗並不是我們需要去處理的。那麼這種時候最好的辦法就是失敗後重試幾次,以避免網絡的間斷性影響。

如果我們正常編寫代碼的話,可能需要 try…except ,但是這種寫法很麻煩,能實現的效果也很單一。這裏介紹一個 Python 庫retrying,專門用來對拋出異常的函數或者方法進行重試。

通過 retrying 你能幹什麼:

  • 出錯後重新運行函數,直到正常運行爲止;
  • 出錯後,暫停一會再運行函數,因爲網絡可能一時半會不會好;
  • 出錯後,如果重試時間過長,會造成代碼效率過低,你可以設置一個最大的重試時間;
  • 出錯時,你可能想去執行另一個函數以排除可能的錯誤。

那接下來我們看看用法,首先你需要安裝這個庫:

pip install retrying

簡單用法

簡單的示例,我們寫一個函數,其中有一個變量 a 從 1 到 2 中隨機取一個, 當 a 爲 1 的時候就拋出異常,我們嘗試在拋出異常時會不會重試:

import random
def demo():
	a = random.randint(1,2)
	print(a)  # => 先打印,好看效果
	if a == 1:
		raise  # 爲1時拋出異常

這個函數有 50% 的概率會拋出錯誤,你可以先試試。不過如果你運氣好的話。。。

好,接下來,我們加上 retrying :

from retrying import retry  # => 引入retry裝飾器
import random

@retry()  # => 裝飾你想重試的函數,注意這裏有括號
def demo():
	a = random.randint(1,2)
	print(a)  # => 先打印,好看效果
	if a == 1:
		raise  # 爲1時拋出異常

demo()   # => 運行這個可能報錯的函數

運行幾次看看,是不是當打印 a 爲 1 的時候會直到 a 爲 2 爲止?也就意味着@rery() 處理了異常並重試。

更多參數

也許你不滿足於讓它默認重試,那麼我們來看看更多的函數。

重試次數

stop_max_attempt_number 最大重試次數,默認爲5次

設定一個最大重試次數,超過這個次數會停止重試,並把異常拋出來。按照你的需要設置吧,一般不要太多就行。或者你也可以不設,默認爲 5 次,不過前提時你修復了下面的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:  # => 這裏我們改爲會始終拋異常的情況,不可能取到3
		raise
demo()
## 1 1 1
## RuntimeError: No active exception to reraise

我們可以看到,運行 3 次都報錯後,第 3 次運行的異常就拋出了。

這裏源代碼有個 bug,如果不設這個參數會無限重試(如果你的異常不會中止就時死循環)。我們來修復這個 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:

時間相關

wait_fixed 重試的間隔時間

當函數拋出異常後,下一次重試會間隔wait_fixed設置的時間。默認是 1000 毫秒(1秒),你可以通過這個參數修改這個默認值。

wait_random_minwait_random_max 用來設置隨機的間隔時間

wait_random_min 和 wait_random_max 搭配起來設置默認的隨機等待時間,默認是 0 ~ 1000 毫秒之間隨機等待。

wait_incrementing_increment 每重試一次,持續增加等待時間
默認是 100 毫秒 ,每重試一次,等待時常就會增加 100 毫秒。

stop_max_delay 最長重試延遲時間,單位毫秒

這個不是重試間隔時間,這是函數運行 + 重試結束的整體時間。比如stop_max_delay=2000也就是說該函數從運行到重試結束爲止的時間爲 2000 毫秒,超過就結束重試並拋出異常。如果你的函數運行時間已經超過 stop_max_delay 時間,就並不會重試。

import time
import random
from retrying import retry

@retry(stop_max_delay=2000)  # => 設置了最大的重試時間
def demo():
    a = random.randint(1, 2)
    print(a, end=" ")
    time.sleep(1)  # => 這裏等待了兩秒
    if a != 3:
        raise
demo()
## 1 2 
## RuntimeError: No active exception to reraise

關聯函數重試

retry_on_result:指定一個函數,如果指定的函數返回True,則重試,否則拋出異常退出。

import random
from retrying import retry

def fun():
	# 處理代碼
    return True  # => 返回True

@retry(retry_on_result=fun) # 指定函數,如果fun返回True則重試,否則不重試
def demo():
    a = random.randint(1, 2)
    print(a, end=" ")
    if a != 3:
        raise
demo()

shop_func:指定被裝飾函數出錯後,會執行的函數,執行該函數後在來重試被裝飾的函數。注意,該函數必須要有兩個參數(attempts, delay)。用來當拋出異常後,需要做一些處理的時候。

a = random.randint(1, 2)

def fun(attempts, delay):
    global a
    a = 3
    print('待測函數')

@retry(stop_func=fun)
def demo():
    print(a, end=" ")
    time.sleep(1)
    if a != 3:
        raise
demo()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章