中文字幕一区二区人妻电影,亚洲av无码一区二区乱子伦as ,亚洲精品无码永久在线观看,亚洲成aⅴ人片久青草影院按摩,亚洲黑人巨大videos

Python sleep():如何向代碼中添加時(shí)間延遲

發(fā)布于:2021-02-03 11:45:20

0

2038

0

python sleep() 時(shí)間延遲

您是否需要讓Python程序等待一些東西?大多數(shù)情況下,您希望代碼能夠盡快執(zhí)行。但有時(shí),讓代碼休眠一段時(shí)間實(shí)際上符合您的最佳利益。

例如,您可以使用Pythonsleep()調(diào)用來模擬程序中的延遲。也許您需要等待文件上傳或下載,或者等待圖形加載或繪制到屏幕上。您甚至可能需要在調(diào)用Web API或查詢數(shù)據(jù)庫之間暫停。sleep()在每種情況下,向程序添加Python調(diào)用都可以提供幫助,甚至更多!

本文的目標(biāo)讀者是希望增加Python知識(shí)的中級(jí)開發(fā)人員。如果這聽起來像你,那我們就開始吧!

使用添加Pythonsleep()調(diào)用time.sleep()

內(nèi)置支持使程序進(jìn)入睡眠狀態(tài)。time模塊有一個(gè)函數(shù)sleep(),您可以使用它來暫停調(diào)用線程的執(zhí)行,不管您指定了多少秒。

下面是一個(gè)如何使用time.sleep()的示例:

>>> import time
>>> time.sleep(3) # Sleep for 3 seconds

如果您在控制臺(tái)中運(yùn)行此代碼,那么您應(yīng)該會(huì)遇到一個(gè)延遲可以在REPL中輸入新語句。

注意:在Python3.5中,核心開發(fā)人員稍微更改了time.sleep()的行為。新的Pythonsleep()系統(tǒng)調(diào)用將至少持續(xù)指定的秒數(shù),即使睡眠被信號(hào)中斷。但是,如果信號(hào)本身引發(fā)異常,則這不適用。

您可以使用Python的timeit模塊測試睡眠持續(xù)時(shí)間:

$ python3 -m timeit -n 3 "import time; time.sleep(3)"
3 loops, best of 3: 3 sec per loop

在這里,您使用-n參數(shù)運(yùn)行timeit模塊,該參數(shù)告訴timeit運(yùn)行語句的次數(shù)接下來就是。您可以看到timeit運(yùn)行語句3次,最佳運(yùn)行時(shí)間是3秒,這是預(yù)期的。

默認(rèn)的timeit運(yùn)行代碼的次數(shù)是一百萬次。如果您使用默認(rèn)的-n運(yùn)行上述代碼,那么每次迭代3秒時(shí),您的終端將掛起大約34天!timeit模塊還有其他幾個(gè)命令行選項(xiàng),您可以在它的文檔中查看這些選項(xiàng)。

讓我們創(chuàng)建一些更真實(shí)的選項(xiàng)。系統(tǒng)管理員需要知道他們的網(wǎng)站何時(shí)關(guān)閉。您希望能夠定期檢查網(wǎng)站的狀態(tài)代碼,但不能經(jīng)常查詢web服務(wù)器,否則會(huì)影響性能。執(zhí)行此檢查的一種方法是使用Pythonsleep()系統(tǒng)調(diào)用:

import time
import urllib.request
import urllib.error

def uptime_bot(url):
   while True:
       try:
           conn = urllib.request.urlopen(url)
       except urllib.error.HTTPError as e:
           # Email admin / log
           print(f'HTTPError: {e.code} for {url}')
       except urllib.error.URLError as e:
           # Email admin / log
           print(f'URLError: {e.code} for {url}')
       else:
           # Website is up
           print(f'{url} is up')
       time.sleep(60)

if __name__ == '__main__':
   url = 'http://www.google.com/py'
   uptime_bot(url)

在這里創(chuàng)建uptime_bot(),它將URL作為其參數(shù)。然后,該函數(shù)嘗試使用urllib打開該URL。如果有一個(gè)HTTPErrorURLError,那么程序?qū)⒉东@它并打印出錯(cuò)誤。(在實(shí)時(shí)環(huán)境中,您將記錄錯(cuò)誤,并可能向網(wǎng)站管理員或系統(tǒng)管理員發(fā)送電子郵件。)

如果沒有出現(xiàn)錯(cuò)誤,那么您的代碼將打印出一切正常。不管發(fā)生什么,你的程序都會(huì)休眠60秒。這意味著你每分鐘只訪問一次網(wǎng)站。本例中使用的URL不正確,因此它將每分鐘向控制臺(tái)輸出一次以下內(nèi)容:

HTTPError: 404 for http://www.google.com/py

繼續(xù)并更新代碼以使用已知的好URL,如http://www.google.com。然后您可以重新運(yùn)行它以查看它是否成功工作。您還可以嘗試更新代碼以發(fā)送電子郵件或記錄錯(cuò)誤。有關(guān)如何執(zhí)行此操作的詳細(xì)信息,請(qǐng)查看使用Python發(fā)送電子郵件和登錄Python。

sleep()使用Decorators添加Python調(diào)用

有時(shí)需要重試失敗的函數(shù)。一個(gè)流行的使用案例是,當(dāng)您因?yàn)榉?wù)器忙而需要重試文件下載時(shí)。您通常不想太頻繁地向服務(wù)器發(fā)出請(qǐng)求,因此在每個(gè)請(qǐng)求之間添加一個(gè)Python調(diào)用是可取的。

我親身經(jīng)歷的另一個(gè)用例是在自動(dòng)測試期間需要檢查用戶界面的狀態(tài)。用戶界面的加載速度可能比平時(shí)快,也可能慢,這取決于我運(yùn)行測試的計(jì)算機(jī)。這會(huì)改變程序正在驗(yàn)證某個(gè)內(nèi)容時(shí)屏幕上的內(nèi)容。

在這種情況下,我可以讓程序休眠片刻,然后在一兩秒鐘后重新檢查。這可能意味著通過測試和失敗測試之間的區(qū)別。

在這兩種情況下,您都可以使用裝飾程序添加Pythonsleep()系統(tǒng)調(diào)用。如果您不熟悉decorators,或者您想對(duì)它們進(jìn)行深入了解,那么請(qǐng)查看Python decorators入門。讓我們看一個(gè)例子:

import time
import urllib.request
import urllib.error

def sleep(timeout, retry=3):
   def the_real_decorator(function):
       def wrapper(*args, **kwargs):
           retries = 0
           while retries < retry:
               try:
                   value = function(*args, **kwargs)
                   if value is None:
                       return
               except:
                   print(f'Sleeping for {timeout} seconds')
                   time.sleep(timeout)
                   retries += 1
       return wrapper
   return the_real_decorator

sleep()是您的裝飾者。它接受一個(gè)timeout值和它應(yīng)該retry的次數(shù),默認(rèn)值為3。在sleep()內(nèi)部是另一個(gè)函數(shù),the_real_decorator(),它接受修飾函數(shù)。

最后,最內(nèi)部的函數(shù)wrapper()接受傳遞給修飾函數(shù)的參數(shù)和關(guān)鍵字參數(shù)。這就是魔法發(fā)生的地方!使用while循環(huán)重試調(diào)用函數(shù)。如果出現(xiàn)異常,則調(diào)用time.sleep(),增加retries計(jì)數(shù)器,然后再次嘗試運(yùn)行該函數(shù)。

現(xiàn)在重寫uptime_bot()以使用新的裝飾器:

@sleep(3)
def uptime_bot(url):
   try:
       conn = urllib.request.urlopen(url)
   except urllib.error.HTTPError as e:
       # Email admin / log
       print(f'HTTPError: {e.code} for {url}')
       # Re-raise the exception for the decorator
       raise urllib.error.HTTPError
   except urllib.error.URLError as e:
       # Email admin / log
       print(f'URLError: {e.code} for {url}')
       # Re-raise the exception for the decorator
       raise urllib.error.URLError
   else:
       # Website is up
       print(f'{url} is up')

if __name__ == '__main__':
   url = 'http://www.google.com/py'
   uptime_bot(url)

這里,您用3秒的sleep()來裝飾uptime_bot()。您還刪除了原始的while循環(huán),以及對(duì)sleep(60)的舊調(diào)用。裝飾程序現(xiàn)在處理這個(gè)問題。

您所做的另一個(gè)更改是在異常處理塊中添加一個(gè)raise。這是為了裝飾工能正常工作。您可以編寫decorator來處理這些錯(cuò)誤,但是由于這些異常只適用于urllib,因此最好保持decorator的原樣。這樣,它將適用于更廣泛的函數(shù)。

注意:如果您想了解Python中的異常處理,請(qǐng)查看Python異常:簡介。

您可以對(duì)decorator進(jìn)行一些改進(jìn)。如果它的重試次數(shù)用完,仍然失敗,那么您可以讓它重新引發(fā)最后一個(gè)錯(cuò)誤。最后一次失敗后,裝飾程序還會(huì)等待3秒鐘,這可能是您不希望發(fā)生的事情。你可以把這些當(dāng)作練習(xí)來嘗試!

sleep()使用線程添加Python調(diào)用

有時(shí)候,您可能想將Pythonsleep()調(diào)用添加到線程中。也許您正在對(duì)具有數(shù)百萬條記錄的數(shù)據(jù)庫運(yùn)行遷移腳本。您不想造成任何停機(jī)時(shí)間,但是您也不想等待比完成遷移所需的時(shí)間更長的時(shí)間,因此決定使用線程。

注意:線程是Python中執(zhí)行并發(fā)的一種方法。您可以一次運(yùn)行多個(gè)線程以提高應(yīng)用程序的吞吐量。如果您不熟悉Python中的線程,請(qǐng)查看Python中的線程簡介。

為了防止客戶注意到任何類型的減速,每個(gè)線程都需要運(yùn)行一段時(shí)間,然后休眠。有兩種方法可以做到這一點(diǎn):

  • 像以前一樣使用time.sleep()。

  • 使用threading模塊中的Event.wait()

讓我們先看看time.sleep()

使用time.sleep()

Python日志記錄烹飪書展示了一個(gè)使用time.sleep()的好例子。Python的logging模塊是線程安全的,因此在本練習(xí)中,它比print()語句更有用。下面的代碼基于此示例:

import logging
import threading
import time

def worker(arg):
   while not arg["stop"]:
       logging.debug("worker thread checking in")
       time.sleep(1)

def main():
   logging.basicConfig(
       level=logging.DEBUG,
       format="%(relativeCreated)6d %(threadName)s %(message)s"
   )
   info = {"stop": False}
   thread = threading.Thread(target=worker, args=(info,))
   thread_two = threading.Thread(target=worker, args=(info,))
   thread.start()
   thread_two.start()

   while True:
       try:
           logging.debug("Checking in from main thread")
           time.sleep(0.75)
       except KeyboardInterrupt:
           info["stop"] = True
           logging.debug('Stopping')
           break
   thread.join()
   thread_two.join()

if __name__ == "__main__":
   main()

這里,您使用Python的threading模塊創(chuàng)建兩個(gè)線程。您還可以創(chuàng)建一個(gè)日志對(duì)象,將threadName日志記錄到stdout。接下來,啟動(dòng)兩個(gè)線程并每隔一段時(shí)間從主線程啟動(dòng)一個(gè)循環(huán)來記錄日志。您可以使用KeyboardInterrupt捕捉用戶按下+

嘗試在終端中運(yùn)行上述代碼。您將看到類似于以下內(nèi)容的輸出:

0 Thread-1 worker thread checking in
1 Thread-2 worker thread checking in
1 MainThread Checking in from main thread
752 MainThread Checking in from main thread
1001 Thread-1 worker thread checking in
1001 Thread-2 worker thread checking in
1502 MainThread Checking in from main thread
2003 Thread-1 worker thread checking in
2003 Thread-2 worker thread checking in
2253 MainThread Checking in from main thread
3005 Thread-1 worker thread checking in
3005 MainThread Checking in from main thread
3005 Thread-2 worker thread checking in

當(dāng)每個(gè)線程運(yùn)行然后休眠時(shí),日志輸出將打印到控制臺(tái)。現(xiàn)在您已經(jīng)嘗試了一個(gè)示例,您將能夠在自己的代碼中使用這些概念。

使用Event.wait()

模塊提供了一個(gè)Event(),您可以像time.sleep()一樣使用它。然而,Event()還有一個(gè)額外的好處,那就是反應(yīng)更快。原因是當(dāng)事件被設(shè)置時(shí),程序?qū)⒘⒓刺鲅h(huán)。使用time.sleep()時(shí),代碼需要等待Pythonsleep()調(diào)用完成,線程才能退出。

之所以要在這里使用wait()是因?yàn)?code>wait()是非阻塞的,而time.sleep()是阻塞的。這意味著,當(dāng)您使用time.sleep()時(shí),將阻止主線程在等待sleep()調(diào)用結(jié)束時(shí)繼續(xù)運(yùn)行。wait()解決了這個(gè)問題。您可以在Python的線程文檔中閱讀更多關(guān)于這些工作原理的信息。

下面是如何使用Event.wait()添加Pythonsleep()調(diào)用:

import logging
import threading

def worker(event):
   while not event.isSet():
       logging.debug("worker thread checking in")
       event.wait(1)

def main():
   logging.basicConfig(
       level=logging.DEBUG,
       format="%(relativeCreated)6d %(threadName)s %(message)s"
   )
   event = threading.Event()

   thread = threading.Thread(target=worker, args=(event,))
   thread_two = threading.Thread(target=worker, args=(event,))
   thread.start()
   thread_two.start()

   while not event.isSet():
       try:
           logging.debug("Checking in from main thread")
           event.wait(0.75)
       except KeyboardInterrupt:
           event.set()
           break

if __name__ == "__main__":
   main()

在本例中,您創(chuàng)建threading.Event()并將其傳遞給worker()。(回想一下,在上一個(gè)示例中,您傳遞了一個(gè)dictionary。)接下來,設(shè)置循環(huán)以檢查是否設(shè)置了event。如果不是,那么代碼將打印一條消息并等待一段時(shí)間,然后再次檢查。要設(shè)置事件,可以按Ctrl+C。設(shè)置事件后,worker()將返回,循環(huán)將中斷,從而結(jié)束程序。

注意:如果您想了解有關(guān)詞典的更多信息,請(qǐng)查看Python中的詞典。

請(qǐng)仔細(xì)查看上面的代碼塊。您如何為每個(gè)工作線程分配不同的睡眠時(shí)間?你能想出來嗎?你可以自己解決這個(gè)問題!

sleep()使用異步IO添加Python調(diào)用

異步功能已在3.4版本中添加到Python,此功能集從那時(shí)以來一直在積極擴(kuò)展。異步編程是一種并行編程,它允許您一次運(yùn)行多個(gè)任務(wù)。任務(wù)完成后,它將通知主線程。

asyncio是一個(gè)允許您異步添加Python調(diào)用的模塊。如果您不熟悉Python的異步編程實(shí)現(xiàn),請(qǐng)查看Python中的異步IO:完整的演練和Python并發(fā)與并行編程。

這里有一個(gè)來自Python自己文檔的示例:

import asyncio

async def main():
   print('Hello ...')
   await asyncio.sleep(1)
   print('... World!')

# Python 3.7+
asyncio.run(main())

在這個(gè)示例中,您運(yùn)行main()并讓它休眠兩次調(diào)用之間的一秒鐘。

以下是asyncio文檔中的協(xié)同程序和任務(wù)部分中一個(gè)更引人注目的示例:

import asyncio
import time

async def output(sleep, text):
   await asyncio.sleep(sleep)
   print(text)

async def main():
   print(f"Started: {time.strftime('%X')}")
   await output(1, 'First')
   await output(2, 'Second')
   await output(3, 'Third')
   print(f"Ended: {time.strftime('%X')}")

# Python 3.7+
asyncio.run(main())

在這段代碼中,您創(chuàng)建了一個(gè)名為output()的工作進(jìn)程,它占用到sleeptext的秒數(shù)打印出來。然后,使用Python的await關(guān)鍵字等待output()代碼運(yùn)行。這里需要await,因?yàn)?code>output()已標(biāo)記為async函數(shù),所以不能像調(diào)用普通函數(shù)那樣調(diào)用它。

運(yùn)行此代碼時(shí),程序?qū)?zhí)行await3次。代碼將等待1、2和3秒,總等待時(shí)間為6秒。您還可以重寫代碼,使任務(wù)并行運(yùn)行:

import asyncio
import time

async def output(text, sleep):
   while sleep > 0:
       await asyncio.sleep(1)
       print(f'{text} counter: {sleep} seconds')
       sleep -= 1

async def main():
   task_1 = asyncio.create_task(output('First', 1))
   task_2 = asyncio.create_task(output('Second', 2))
   task_3 = asyncio.create_task(output('Third', 3))
   print(f"Started: {time.strftime('%X')}")
   await task_1
   await task_2
   await task_3                                
   print(f"Ended: {time.strftime('%X')}")

if __name__ == '__main__':
   asyncio.run(main())

現(xiàn)在您使用的是任務(wù)的概念,您可以使用create_task()來創(chuàng)建它。在asyncio中使用任務(wù)時(shí),Python將異步運(yùn)行這些任務(wù)。因此,當(dāng)您運(yùn)行上述代碼時(shí),它應(yīng)該在3秒內(nèi)完成,而不是6秒。

sleep()使用GUI添加Python調(diào)用

命令行應(yīng)用程序并不是唯一需要添加Pythonsleep()調(diào)用的地方。創(chuàng)建圖形用戶界面(GUI)時(shí),有時(shí)需要添加延遲。例如,您可以創(chuàng)建一個(gè)FTP應(yīng)用程序來下載數(shù)百萬個(gè)文件,但您需要在批處理之間添加一個(gè)調(diào)用,這樣就不會(huì)使服務(wù)器陷入困境。

GUI代碼將在一個(gè)名為事件循環(huán)的主線程中運(yùn)行其所有處理和繪圖。如果在GUI代碼中使用time.sleep(),則會(huì)阻塞其事件循環(huán)。從用戶的角度來看,應(yīng)用程序可能會(huì)凍結(jié)。當(dāng)應(yīng)用程序使用此方法休眠時(shí),用戶將無法與應(yīng)用程序交互。(在Windows上,您甚至可能會(huì)收到有關(guān)應(yīng)用程序現(xiàn)在如何無響應(yīng)的警報(bào)。)

幸運(yùn)的是,除了time.sleep()之外,您還可以使用其他方法。在接下來的幾節(jié)中,您將學(xué)習(xí)如何在Tkinter和wxPython中添加Python調(diào)用。如果您在Linux或Mac上使用的是Python的預(yù)裝版本,那么它可能不可用。如果您得到一個(gè)ImportError,那么您需要研究如何將它添加到您的系統(tǒng)中。但是如果您自己安裝Python,那么應(yīng)該已經(jīng)可以使用了。運(yùn)行此代碼以查看添加Python調(diào)用時(shí)發(fā)生的情況:

import tkinter
import time

class MyApp:
    def __init__(self, parent):
        self.root = parent
        self.root.geometry("400x400")
        self.frame = tkinter.Frame(parent)
        self.frame.pack()
        b = tkinter.Button(text="click me", command=self.delayed)
        b.pack()

    def delayed(self):
        time.sleep(3)

if __name__ == "__main__":
    root = tkinter.Tk()
    app = MyApp(root)
    root.mainloop()

運(yùn)行代碼后,按GUI中的按鈕。當(dāng)按鈕等待sleep()完成時(shí),它將向下停留3秒鐘。如果應(yīng)用程序有其他按鈕,那么您將無法單擊它們。您也不能在應(yīng)用程序睡眠時(shí)關(guān)閉它,因?yàn)樗鼰o法響應(yīng)關(guān)閉事件。

要使tkinter正常睡眠,您需要使用after()

import tkinter

class MyApp:
    def __init__(self, parent):
        self.root = parent
        self.root.geometry("400x400")
        self.frame = tkinter.Frame(parent)
        self.frame.pack()
        self.root.after(3000, self.delayed)

    def delayed(self):
        print('I was delayed')

if __name__ == "__main__":
    root = tkinter.Tk()
    app = MyApp(root)
    root.mainloop()

在這里創(chuàng)建一個(gè)寬400像素、高400像素的應(yīng)用程序。它上面沒有小部件。它只會(huì)顯示一個(gè)框架。然后,調(diào)用self.root.after(),其中self.root是對(duì)Tk()對(duì)象的引用。after()接受兩個(gè)參數(shù):

  1. 睡眠的毫秒數(shù)

  2. 睡眠完成時(shí)要調(diào)用的方法

在這種情況下,應(yīng)用程序?qū)⒃?秒鐘后將字符串打印到標(biāo)準(zhǔn)輸出。您可以將after()視為tkinter版本的time.sleep(),但它還增加了在睡眠結(jié)束后調(diào)用函數(shù)的功能。

您可以使用此功能來改善用戶體驗(yàn)。通過添加Pythonsleep()調(diào)用,可以使應(yīng)用程序看起來加載更快,然后在啟動(dòng)后啟動(dòng)一些運(yùn)行時(shí)間更長的進(jìn)程。那樣的話,用戶無需等待應(yīng)用程序打開。

睡眠在wxPython

wxPython和Tkinter之間有兩個(gè)主要區(qū)別:

  1. wxPython有更多的小部件。

  2. wxPython的目標(biāo)是在所有平臺(tái)上看起來和感覺都是本地的。

Python中沒有wxPython框架,所以您需要自己安裝它。如果您不熟悉wxPython,請(qǐng)查看如何使用wxPython構(gòu)建Python GUI應(yīng)用程序。

在wxPython中,您可以使用wx.CallLater()添加Python調(diào)用:

import wx

class MyFrame(wx.Frame):
   def __init__(self):
       super().__init__(parent=None, title='Hello World')
       wx.CallLater(4000, self.delayed)
       self.Show()

   def delayed(self):
       print('I was delayed')

if __name__ == '__main__':
   app = wx.App()
   frame = MyFrame()
   app.MainLoop()

在這里,您可以直接將wx.Frame子類化,然后調(diào)用wx.CallLater()。此函數(shù)采用與Tkinter的after()相同的參數(shù):

  1. 睡眠的毫秒數(shù)

  2. 睡眠完成時(shí)調(diào)用的方法

運(yùn)行此代碼時(shí),應(yīng)該會(huì)看到一個(gè)沒有任何小部件的小空白窗口出現(xiàn)。4秒鐘后,您將看到字符串'I was delayed'打印到stdout。

使用wx.CallLater()的好處之一是線程安全。您可以在線程中使用此方法來調(diào)用wxPython主應(yīng)用程序中的函數(shù)。

結(jié)論

通過本教程,您獲得了一項(xiàng)有價(jià)值的新技術(shù),可以添加到Python工具箱中!您知道如何添加延遲來加快應(yīng)用程序的速度,并防止它們耗盡系統(tǒng)資源。您甚至可以使用Pythonsleep()調(diào)用來幫助更有效地重新繪制GUI代碼。這將為您的客戶提供更好的用戶體驗(yàn)!