簡體   English   中英

如何在 Python 的 for 循環中創建超時?

[英]How do you create a timeout in a for-loop in Python?

我有一個從 API 檢索數據的 for 循環:

app = WebService()
for i in items:
    result = app.request(item)

我想創建一個超時,這樣,如果app.request阻塞調用花費太長時間,循環將跳過它,go 到下一個項目。

我已經閱讀了一些使用while循環創建計時器的方法,但我相信在我的情況下,我無法在 for 循環中創建一個while循環,並使用適用for forcontinue子句......那么,我該怎么做做這個?

不幸的是,API 沒有提供創建超時的方法。 它不是對 REST API 的 HTTP 調用。

基於這個答案

這是一個很好的裝飾器用例。 當您想要包裝具有附加功能的 function 或 class 方法時,裝飾器模式很有用。

這相當於如何使用 Python 的signal庫執行 Python 3 超時裝飾器。

一旦你有了裝飾器,將你的 app.request 包裝在一個decorated過的 function 中,並使用 try-except 處理異常。


# main.py
import signal

DEBUG = True

# Custom exception. In general, it's a good practice.
class TimeoutError(Exception):
    def __init__(self, value = "Timed Out"):
        self.value = value
    def __str__(self):
        return repr(self.value)

# This is the decorator itself.
# Read about it here: https://pythonbasics.org/decorators/
# Basically, it's a function that receives another function and a time parameter, i.e. the number of seconds.
# Then, it wraps it so that it raises an error if the function does not
# return a result before `seconds_before_timeout` seconds
def timeout(seconds_before_timeout):
  def decorate(f):
    if DEBUG: print("[DEBUG]: Defining decorator handler and the new function")
    if DEBUG: print(f"[DEBUG]: Received the following function >> `{f.__name__}`")
    def handler(signum, frame):
      raise TimeoutError()
    def new_f(*args, **kwargs):
      # Verbatim from Python Docs
      # > The signal.signal() function allows defining custom handlers to be executed
      #   when a signal is received.
      if DEBUG: print(f"[DEBUG]: in case of ALARM for function `{f.__name__}`, I'll handle it with the... `handler`")
      old = signal.signal(signal.SIGALRM, handler)

      # See https://docs.python.org/3/library/signal.html#signal.alarm
      if DEBUG: print(f"[DEBUG]: setting an alarm for {seconds_before_timeout} seconds.")
      signal.alarm(seconds_before_timeout)
      try:
          if DEBUG: print(f"[DEBUG]: executing `{f.__name__}`...")
          result = f(*args, **kwargs)
      finally:
          # reinstall the old signal handler
          signal.signal(signal.SIGALRM, old)
          # Cancel alarm. 
          # See: https://docs.python.org/3/library/signal.html#signal.alarm
          signal.alarm(0)
      return result
    
    new_f.__name__ = f.__name__
    return new_f
  return decorate

import time

@timeout(5)
def mytest():
    for i in range(1,10):
      interval = 2
      if DEBUG: print("[DEBUG]: waiting 2 seconds... on purpose")
      time.sleep(interval)
      print("%d seconds have passed" % (interval * i))

if __name__ == '__main__':
  if DEBUG: print("[DEBUG]: Starting program")
  mytest()

您可以在此 repl.it上快速嘗試


此外,如果您不想在 function 中“隱藏” API 調用,則可以進行依賴倒置。 也就是說,您裝飾的 function 不依賴於任何request function 實現。 為此,您收到 function 本身作為參數。 見下文:

# simple decoration
@timeout(5)
def make_request(item):
    # assuming `app` is defined
    return app.request(item)

# dependency inversion

@timeout(5)
def make_request(request_handler, item):
    return request_handler(item)

# and then in your loop...
for i in items:
    make_request(app.request, item)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM