裝飾器
關于裝飾器的入門,可以參考這篇文章: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ù)超時機制