繁体   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