Python的自定義超時機制——裝飾器的妙用

裝飾器

關于裝飾器的入門,可以參考這篇文章:12步輕松搞定python裝飾器
簡單來說,裝飾器其實就是一個閉包(閉包 – 被函數(shù)記住的封閉作用域 – 能夠被用來創(chuàng)建自定義的函數(shù)),把一個函數(shù)當做參數(shù)然后返回一個替代版的函數(shù),在Python里面,函數(shù)也是對象。實際上裝飾器就是一個加強的函數(shù),目的是為了更簡潔地加強函數(shù)的同時并且不影響函數(shù)的內部。
那么我們想實現(xiàn)超時中止機制的時候,是不是可以考慮給某函數(shù)func增加一個裝飾器,讓裝飾器實現(xiàn)超時中止的功能,這樣還能把裝飾器用在其他的函數(shù)里面,同時我們的func函數(shù)的內部也不會有額外的代碼。
有空會補充裝飾器的細節(jié),或者另開一篇文章。

信號

在實現(xiàn)超時中止的裝飾器之前,先引入一下Python的信號(signal)機制。
信號機制的作用:發(fā)送和接收異步系統(tǒng)信號
信號是一個操作系統(tǒng)特性,它提供了一個途徑可以通知程序發(fā)生了一個事件并異步處理這個事件。信號可以由系統(tǒng)本身生成,也可以從一個進程發(fā)送到另一個進程。
信號實際上是進程之間通訊的方式,是一種軟件中斷。一個進程一旦接收到信號就會打斷原來的程序執(zhí)行流程來處理信號。
Python標準庫中的signal包負責在Python程序內部處理信號,典型的操作包括預設信號處理函數(shù),暫停并等待信號,以及定時發(fā)出SIGALRM等。要注意,signal包主要是針對UNIX平臺(比如Linux, MAC OS),而Windows內核中由于對信號機制的支持不充分,所以在Windows上的Python不能發(fā)揮信號系統(tǒng)的功能。
下面簡單介紹一下這次會用到的signal包的相關方法,更多的細節(jié)可以參考:

signal包的核心是使用signal.signal()函數(shù)來預設(register)信號處理函數(shù),如下所示:

import signal
'''
python 的信號名與Linux系統(tǒng)一致,
查看你的linux支持哪些信號:kill -l 即可
SIGINT 終止進程 中斷進程 (control+c) 
SIGTERM 終止進程 軟件終止信號 
SIGKILL 終止進程 殺死進程 
SIGALRM 鬧鐘信號
'''
signal.signal(signal_num, handler)
'''
signal_num為某個信號,handler為該信號的處理函數(shù),或者說是回調函數(shù)。
進程可以無視信號,可以采取默認操作,還可以自定義操作。
當handler為signal.SIG_IGN時,信號被無視(ignore)。
當handler為singal.SIG_DFL,進程采取默認操作(default)。
當handler為一個函數(shù)名時,進程采取函數(shù)中定義的操作。
'''
signal.alarm(seconds) #如果time 非0,這個函數(shù)則響應一個SIGALRM信號并在time秒后發(fā)送到該進程。
signal.alarm(0) #假如在callback函數(shù)未執(zhí)行的時候,要取消的話,那么可以使用alarm(0)來取消調用該回調函數(shù)

實現(xiàn)

有了上面的信號基礎,我們就可以用signal.alarm(t)來做超時中止,實現(xiàn)思路簡單來說就是,在執(zhí)行某函數(shù)func前,設定好一個seconds時間的鬧鐘信號signal.alarm(t)。這也就是意味著如果超時(也就是t秒后),會有一個信號激發(fā)信號的回調函數(shù)handler,這樣只要回調函數(shù)可以引發(fā)異常就可以實現(xiàn)超時中止功能了;如果沒有超時,則在func運行結束后使用signal.alarm(0) 取消回調函數(shù)handler的執(zhí)行。

import signal,functools
class TimeoutError(Exception):pass #定義一個超時錯誤類
def time_out(seconds,error_msg='TIME_OUT_ERROR:No connection were found in limited time!'):
#帶參數(shù)的裝飾器
    def decorated(func):
        result = ''
        def signal_handler(signal_num,frame): # 信號機制的回調函數(shù),signal_num即為信號,frame為被信號中斷那一時刻的棧幀
            global result
            result = error_msg
            raise TimeoutError(error_msg) #raise顯式地引發(fā)異常。一旦執(zhí)行了raise語句,raise后面的語句將不能執(zhí)行
        
        def wrapper(*args,**kwargs):  #def wrapper(func,*args,**kwargs):
            global result
            signal.signal(signal.SIGALRM, signal_handler)
            signal.alarm(seconds) #如果time是非0,這個函數(shù)則響應一個SIGALRM信號并在time秒后發(fā)送到該進程。
            try:
                result = func(*args,**kwargs) 
                #若超時,此時alarm會發(fā)送信息激活回調函數(shù)signal_handler,從而引發(fā)異常終止掉try的代碼塊
            finally:
                signal.alarm(0) #假如在callback函數(shù)未執(zhí)行的時候,要取消的話,那么可以使用alarm(0)來取消調用該回調函數(shù)
                print('finish')
                return result
        return functools.wraps(func)(wrapper) #return wrapper 
    return decorated

import time

@time_out(5) #給func設定了超時時間為5s
def func():
    #可以插入http請求等代碼
    time.sleep(10) #模擬超時
    return

#調用func
func()

后話

超時中止機制還可以配合threading等實現(xiàn)超時kill的效果
比如:論 Python 裝飾器控制函數(shù) Timeout 的正確姿勢

參考文章:

Understanding Python Decorators in 12 Easy Steps!
12步輕松搞定python裝飾器
深入淺出 Python 裝飾器:16 步輕松搞定 Python 裝飾器
深入淺出 Python 裝飾器:16 步輕松搞定 Python 裝飾器
Python模塊之信號signal
論 Python 裝飾器控制函數(shù) Timeout 的正確姿勢
python裝飾器:三種函數(shù)超時機制

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • Python 是一門極簡的語言,語言簡潔學習起來也是相當輕松的,但是依然有一些高級技巧,例如裝飾器、協(xié)程、并發(fā),會...
    妄心xyx閱讀 1,697評論 0 23
  • 部分細節(jié)自己改了點,也加了點自己例子,基本上屬于轉載。轉載出處:https://my.oschina.net/le...
    洛克黃瓜閱讀 2,114評論 0 3
  • 本文為《爬著學Python》系列第四篇文章。從本篇開始,本專欄在順序更新的基礎上,會有不規(guī)則的更新。 在Pytho...
    SyPy閱讀 2,577評論 4 11
  • 死亡瞬間發(fā)生 菜市場上的屠夫,劊子手 玻璃缸里揪出蹦噠的魚 棒打頭暈,從容淡定宰殺,刀刀鱗片淌血 忽然間幻影,刑場...
    忠志_3d7b閱讀 592評論 1 5
  • 丟失的星星文/悠葉 秋是少女懵懂的愛情凋零是注定的結局 雨用力清洗天空的眼睛為迷失的彩虹找到 回家的軌跡 風穿過...
    悠葉_閱讀 3,391評論 94 72

友情鏈接更多精彩內容