av激情亚洲男人的天堂国语,日韩欧美精品一中文字幕,无码av一区二区三区无码,国产又色又爽又刺激的a片,国产又色又爽又刺激的a片

無所不能的Python竟然沒有一個像樣的定時器?試試這個!

本文轉(zhuǎn)載自微信公眾號「Python作業(yè)輔導(dǎo)員」,作者天元浪子。轉(zhuǎn)載本文請聯(lián)系Python作業(yè)輔導(dǎo)員公眾號。

所謂定時器,是指間隔特定時間執(zhí)行特定任務(wù)的機制。幾乎所有的編程語言,都有定時器的實現(xiàn)。比如,Java有util.Timer和util.TimerTask,JavaScript有setInterval和setTimeout,可以實現(xiàn)非常復(fù)雜的定時任務(wù)處理。然而,牛叉到無所不能的Python,卻沒有一個像樣的定時器,實在令人難以理解。

剛?cè)腴T的同學(xué)一定會說:不是有個time.sleep嗎?定好鬧鐘睡大覺,鬧鐘一響,起來干活,這不就是一個定時器嗎?沒錯,time.sleep具備定時器的基本要素,但若作為定時器使用,則有兩個致命的缺陷:一是阻塞主線程,睡覺的時候不能做任何事情;二是醒來以后需要主線程執(zhí)行定時任務(wù)——即便使用線程技術(shù),也得先由主線程來創(chuàng)建子線程。

說到這里,熟悉線程模塊threading的同學(xué)也許會說:threading.Timer就是以線程方式運行的呀,既不會阻塞主線程,執(zhí)行定時任務(wù)也無需主線程干預(yù),這不就是一個完美的定時器嗎?

我們先來看看threading.Timer是如何工作的。下面這段代碼演示了threading.Timer的基本用法:啟動定時器2秒鐘后以線程方式調(diào)用函數(shù)do_something,在定時器等待的2秒鐘內(nèi),以及do_something運行期間,主線程仍然可以做其他工作——此處是從鍵盤讀取輸入,借以阻塞主線程,以便觀察定時器的工作情況。

 
 
 
 
  1. import time 
  2. import threading 
  3.  
  4. def do_something(name, gender='male'): 
  5.     print(time.time(), '定時時間到,執(zhí)行特定任務(wù)' ) 
  6.     print('name:%s, gender:%s'%(name, gender)) 
  7.  
  8. timer = threading.Timer(2, do_something, args=('Alice',), kwargs={'gender':'female'}) 
  9. timer.start() 
  10. print(time.time(), '定時開始時間') 
  11. input('按回車鍵結(jié)束\n') # 此處阻塞主進程 

正如我們所期待的那樣,定時器啟動2秒鐘后,函數(shù)do_something被調(diào)用,這期間可以隨時敲擊回車鍵結(jié)束程序。這段代碼的運行結(jié)果如下。

 
 
 
 
  1. 1627438957.4297626 定時開始時間 
  2. 按回車鍵結(jié)束 
  3. 1627438959.4299397 定時時間到,執(zhí)行特定任務(wù) 
  4. name:Alice, gender:female 

從使用效果看,threading.Timer稱得上是一款簡潔易用的定時器。不過,threading.Timer存在明顯的短板,那就是不支持連續(xù)的定時任務(wù),比如,每隔2秒鐘調(diào)用一次do_something函數(shù)。如果一定要用threading.Timer實現(xiàn)連續(xù)定時,只能用類似嵌套的變通方法,在do_something函數(shù)中再次啟動定時器。

 
 
 
 
  1. import time 
  2. import threading 
  3.  
  4. def do_something(name, gender='male'): 
  5.     global timer 
  6.     timer = threading.Timer(2, do_something, args=(name,), kwargs={'gender':gender}) 
  7.     timer.start() 
  8.  
  9.     print(time.time(), '定時時間到,執(zhí)行特定任務(wù)' ) 
  10.     print('name:%s, gender:%s'%(name, gender)) 
  11.     time.sleep(5) 
  12.     print(time.time(), '完成特定任務(wù)' ) 
  13.  
  14. timer = threading.Timer(2, do_something, args=('Alice',), kwargs={'gender':'female'}) 
  15. timer.start() 
  16. input('按回車鍵結(jié)束\n') # 此處阻塞主進程 

這段代碼重新定義了do_something函數(shù),在函數(shù)開始位置啟動下一次的定時任務(wù)。之所以放在開始位置,是為了保證兩次定時之間的時間間隔盡可能精確。饒是如此,下面的運行結(jié)果顯示,兩次定時之間的時間間隔比設(shè)計的2秒鐘多了大約10毫秒,且誤差是連續(xù)累計的,重復(fù)執(zhí)行100次,誤差將會超過1秒鐘。

 
 
 
 
  1. 按回車鍵結(jié)束 
  2. 1627440628.683803 定時時間到,執(zhí)行特定任務(wù) 
  3. name:Alice, gender:female 
  4. 1627440630.6929214 定時時間到,執(zhí)行特定任務(wù) 
  5. name:Alice, gender:female 
  6. 1627440632.707388 定時時間到,執(zhí)行特定任務(wù) 
  7. name:Alice, gender:female 
  8. 1627440633.6890671 完成特定任務(wù) 
  9. 1627440634.722474 定時時間到,執(zhí)行特定任務(wù) 
  10. name:Alice, gender:female 
  11. 1627440635.7092102 完成特定任務(wù) 
  12. 1627440636.7277966 定時時間到,執(zhí)行特定任務(wù) 
  13. name:Alice, gender:female 

針對連續(xù)的定時任務(wù),threading.Timer的表現(xiàn)還算差強人意,只是這種嵌套的寫法完全顛覆了代碼美學(xué)。對于像我這樣有代碼潔癖的程序員來說,是無法容忍和不可接受的。在我看來,一個完美的定時器應(yīng)該滿足以下5個條件,具備下圖所示的結(jié)構(gòu)。

  1. 不阻塞主線程
  2. 同時支持單次定時和連續(xù)定時
  3. 以線程或進程方式執(zhí)行定時任務(wù)
  4. 定時任務(wù)的線程或進程的創(chuàng)建、運行,不影響定時精度
  5. 足夠精確的定時精度,且誤差不會累計

既然Python沒有提供一個像樣的定時器,那就自己寫一個吧。下面這個定時器,滿足上面提到的5個條件,最短時間間隔可以低至10毫秒,且誤差不會累計。雖然還不夠完美,但無論結(jié)構(gòu)還是精度,都還說得過去。

 
 
 
 
  1. import time 
  2. import threading 
  3.  
  4. class PyTimer: 
  5.     """定時器類""" 
  6.  
  7.     def __init__(self, func, *args, **kwargs): 
  8.         """構(gòu)造函數(shù)""" 
  9.  
  10.         self.func = func 
  11.         self.args = args 
  12.         self.kwargs = kwargs 
  13.         self.running = False 
  14.  
  15.     def _run_func(self): 
  16.         """運行定時事件函數(shù)""" 
  17.  
  18.         th = threading.Thread(target=self.func, args=self.args, kwargs=self.kwargs) 
  19.         th.setDaemon(True) 
  20.         th.start() 
  21.  
  22.     def _start(self, interval, once): 
  23.         """啟動定時器的線程函數(shù)""" 
  24.  
  25.         if interval < 0.010: 
  26.             interval = 0.010 
  27.  
  28.         if interval < 0.050: 
  29.             dt = interval/10 
  30.         else: 
  31.             dt = 0.005 
  32.  
  33.         if once: 
  34.             deadline = time.time() + interval 
  35.             while time.time() < deadline: 
  36.                 time.sleep(dt) 
  37.  
  38.             # 定時時間到,調(diào)用定時事件函數(shù) 
  39.             self._run_func() 
  40.         else: 
  41.             self.running = True 
  42.             deadline = time.time() + interval 
  43.             while self.running: 
  44.                 while time.time() < deadline: 
  45.                     time.sleep(dt) 
  46.  
  47.                 # 更新下一次定時時間 
  48.                 deadline += interval 
  49.  
  50.                 # 定時時間到,調(diào)用定時事件函數(shù) 
  51.                 if self.running: 
  52.                     self._run_func() 
  53.  
  54.     def start(self, interval, once=False): 
  55.         """啟動定時器 
  56.  
  57.         interval    - 定時間隔,浮點型,以秒為單位,最高精度10毫秒 
  58.         once        - 是否僅啟動一次,默認是連續(xù)的 
  59.         """ 
  60.  
  61.         th = threading.Thread(target=self._start, args=(interval, once)) 
  62.         th.setDaemon(True) 
  63.         th.start() 
  64.  
  65.     def stop(self): 
  66.         """停止定時器""" 
  67.  
  68.         self.running = False 

定時器類PyTimer實例化時,需要傳入定時任務(wù)函數(shù)。如果定時任務(wù)函數(shù)有參數(shù),也可以按照位置參數(shù)、關(guān)鍵字參數(shù)的順序一并提供。PyTimer定時器提供start和stop兩個方法,用于啟動和停止定時器。其中stop方法不需要參數(shù),start則需要一個以秒為單位的定時間隔參數(shù)。start還有一個布爾型的默認參數(shù)once,可以設(shè)置是否單次定時。once參數(shù)的默認值為False,即默認連續(xù)定時;如果需要單次定時,只需要將once置為true即可。

 
 
 
 
  1. def do_something(name, gender='male'): 
  2.     print(time.time(), '定時時間到,執(zhí)行特定任務(wù)' ) 
  3.     print('name:%s, gender:%s'%(name, gender)) 
  4.     time.sleep(5) 
  5.     print(time.time(), '完成特定任務(wù)' ) 
  6.  
  7. timer = PyTimer(do_something, 'Alice', gender='female') 
  8. timer.start(0.5, once=False) 
  9.  
  10. input('按回車鍵結(jié)束\n') # 此處阻塞主進程 
  11. timer.stop() 

上面是使用PyTimer定時器以0.5秒鐘的間隔連續(xù)調(diào)用函數(shù)do_something的例子。這段代碼的運行結(jié)果如下。

 
 
 
 
  1. 按回車鍵結(jié)束 
  2. 1627450313.425347 定時時間到,執(zhí)行特定任務(wù) 
  3. name:Alice, gender:female 
  4. 1627450313.9226055 定時時間到,執(zhí)行特定任務(wù) 
  5. name:Alice, gender:female 
  6. 1627450314.421761 定時時間到,執(zhí)行特定任務(wù) 
  7. name:Alice, gender:female 
  8. 1627450314.9243422 定時時間到,執(zhí)行特定任務(wù) 
  9. name:Alice, gender:female 
  10. 1627450315.422722 定時時間到,執(zhí)行特定任務(wù) 
  11. name:Alice, gender:female 
  12. 1627450315.9200313 定時時間到,執(zhí)行特定任務(wù) 
  13. name:Alice, gender:female 
  14. 1627450316.4204514 定時時間到,執(zhí)行特定任務(wù) 
  15. name:Alice, gender:female 
  16. 1627450316.9215539 定時時間到,執(zhí)行特定任務(wù) 
  17. name:Alice, gender:female 
  18. 1627450317.4228196 定時時間到,執(zhí)行特定任務(wù) 
  19. name:Alice, gender:female 
  20. 1627450317.9245899 定時時間到,執(zhí)行特定任務(wù) 
  21. name:Alice, gender:female 
  22. 1627450318.42355 定時時間到,執(zhí)行特定任務(wù) 
  23. name:Alice, gender:female 
  24. 1627450318.4393418 完成特定任務(wù) 
  25. 1627450318.9251466 定時時間到,執(zhí)行特定任務(wù) 
  26. name:Alice, gender:female 
  27. 1627450318.9395308 完成特定任務(wù) 
  28. 1627450319.4242043 完成特定任務(wù) 
  29. 1627450319.4242043 定時時間到,執(zhí)行特定任務(wù) 
  30. name:Alice, gender:female 
  31. 1627450319.9253905 定時時間到,執(zhí)行特定任務(wù) 
  32. name:Alice, gender:female 
  33. 1627450319.9411068 完成特定任務(wù) 
  34. 1627450320.425871 完成特定任務(wù) 
  35. 1627450320.425871 定時時間到,執(zhí)行特定任務(wù) 
  36. name:Alice, gender:female 

雖然每個定時任務(wù)需要運行5秒鐘,但每隔0.5秒都會準(zhǔn)時啟動一個新的線程運行定時任務(wù)。從記錄可以看出,盡管每次定時任務(wù)的啟動時間有幾個毫秒的誤差,但誤差不會累計,重復(fù)執(zhí)行的時間間隔均值始終穩(wěn)定在0.5秒。   【責(zé)任編輯: 武曉燕 TEL:(010)68476606】


名稱欄目:無所不能的Python竟然沒有一個像樣的定時器?試試這個!
轉(zhuǎn)載來源:http://uogjgqi.cn/article/dpegghe.html
掃二維碼與項目經(jīng)理溝通

我們在微信上24小時期待你的聲音

解答本文疑問/技術(shù)咨詢/運營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流