我发现这个装饰器在Stack Overflow上超时了一个函数,我想知道是否有人可以详细解释它是如何工作的,因为代码非常优雅但根本不清楚.用法是@timeout(timelimit).
from functools import wraps
import errno
import os
import signal
class TimeoutError(Exception):
pass
def timeout(seconds=100, error_message=os.strerror(errno.ETIME)):
def decorator(func):
def _handle_timeout(signum, frame):
raise TimeoutError(error_message)
def wrapper(*args, **kwargs):
signal.signal(signal.SIGALRM, _handle_timeout)
signal.alarm(seconds)
try:
result = func(*args, **kwargs)
finally:
signal.alarm(0)
return result
return wraps(func)(wrapper)
return decorator
Run Code Online (Sandbox Code Playgroud)
Aar*_*all 14
@timeout(timelimit)装饰器如何工作?
为了更清楚,基于问题中的示例,用法如下:
@timeout(100)
def foo(arg1, kwarg1=None):
'''time this out!'''
something_worth_timing_out()
Run Code Online (Sandbox Code Playgroud)
以上是装饰器语法.以下是语义上等同的:
def foo(arg1, kwarg1=None):
'''time this out!'''
something_worth_timing_out()
foo = timeout(100)(foo)
Run Code Online (Sandbox Code Playgroud)
请注意,我们将包装原始foo的函数命名为" foo".这就是装饰器语法的含义和作用.
from functools import wraps
import errno
import os
import signal
Run Code Online (Sandbox Code Playgroud)
class TimeoutError(Exception):
pass
Run Code Online (Sandbox Code Playgroud)
这就是行中所谓的@timeout(timelimit).该timelimit参数将被锁定到内部功能,使这些功能的"倒闭潮",所谓的,因为他们近距离过来的数据:
def timeout(seconds=100, error_message=os.strerror(errno.ETIME)):
Run Code Online (Sandbox Code Playgroud)
这将返回一个函数,该函数将函数作为参数,下一行继续定义.此函数将返回包装原始函数的函数.:
def decorator(func):
Run Code Online (Sandbox Code Playgroud)
这是一个超时装饰函数的函数:
def _handle_timeout(signum, frame):
raise TimeoutError(error_message)
Run Code Online (Sandbox Code Playgroud)
这是实际的包装器.在调用包装函数之前,它会设置一个信号,如果它没有及时完成并且异常,它将中断该函数:
def wrapper(*args, **kwargs):
signal.signal(signal.SIGALRM, _handle_timeout)
signal.alarm(seconds)
try:
result = func(*args, **kwargs)
finally:
signal.alarm(0)
Run Code Online (Sandbox Code Playgroud)
如果函数完成,这将返回结果:
return result
Run Code Online (Sandbox Code Playgroud)
这将返回包装器.它确保包装函数从原始函数中获取属性,如docstrings,name,function signature ...
return wraps(func)(wrapper)
Run Code Online (Sandbox Code Playgroud)
这是从原始调用返回装饰器的地方@timeout(timelimit):
return decorator
Run Code Online (Sandbox Code Playgroud)
wrapswraps函数允许包装目标函数的函数获取该函数的文档,因为foo不再指向原始函数:
>>> help(foo)
Help on function foo in module __main__:
foo(arg1, kwarg1=None)
time this out!
Run Code Online (Sandbox Code Playgroud)
wraps为了进一步说明,wrapps返回一个装饰器,并且打算像这个函数一样使用.它会更好地写成这样:
def timeout(seconds=100, error_message=os.strerror(errno.ETIME)):
def decorator(func):
def _handle_timeout(signum, frame):
raise TimeoutError(error_message)
@wraps(func)
def wrapper(*args, **kwargs):
signal.signal(signal.SIGALRM, _handle_timeout)
signal.alarm(seconds)
try:
result = func(*args, **kwargs)
finally:
signal.alarm(0)
return result
return wrapper
return decorator
Run Code Online (Sandbox Code Playgroud)