在timeit内调用Python函数超时

Timeout on a Python function call inside timeit

我有一个函数,我们称之为my_function(*args, **kwargs),根据传递给它的参数,它需要30秒到很多小时(天)。我有一个不同参数的列表,我想知道函数在给定参数的情况下需要多长时间。

我仍然很擅长使用timeit,但我已经学到了足够的知识来做这个部分;但是,这对我的目的来说不是很有效。尽管所有这些参数都可以在"有限"时间内解决,但任何需要超过4小时才能完成的参数都会被认为是难以处理的。由于一些(可能是大多数)参数将导致运行时间超过20小时,我正在寻找一种方法来在4小时后终止测试,这样我就不必浪费时间来搞清楚这是棘手的。

我在Python函数调用和停止代码之后查看了Timeout,这可能是一个问题的重复,但我无法将这些答案与timeit集成,因此时间少于4小时记录就像它们应该是长时间运行返回大于4小时的有效时间。

这样做最好的方法是什么?

编辑:我遇到的一个问题是,我看到的答案是func(*args,**kwargs),而timeit函数看起来像这样:

1
timeit.Timer('my_function(*args, **kwargs)', setup=setup).timeit(1)

我不知道如何处理这个表格。

编辑:我在下面使用线程提供的原始答案实际上并不终止线程。通过以下功能运行它可以很容易地显示出来。

1
2
3
4
5
def foo(x):
    for i in xrange(1, x + 1):
        sleep(1)
        print i
    return x

使用涉及multiprocessing.Pool的代码(实际上具有terminate())允许这样做。


根据在Timeout函数中找到的答案,在python中使用线程不起作用。 如果你在foo(x)上尝试它,它确实会停止计数,这与我以前的答案不同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import multiprocessing as mp
import timeit

def timeout(func, args=(), kwargs=None, TIMEOUT=10, default=None, err=.05):

    if hasattr(args,"__iter__") and not isinstance(args, basestring):
        args = args
    else:
        args = [args]    
    kwargs = {} if kwargs is None else kwargs

    pool = mp.Pool(processes = 1)

    try:
        result = pool.apply_async(func, args=args, kwds=kwargs)
        val = result.get(timeout = TIMEOUT * (1 + err))
    except mp.TimeoutError:
        pool.terminate()
        return default
    else:
        pool.close()
        pool.join()
        return val

def Timeit(command, setup=''):
    return timeit.Timer(command, setup=setup).timeit(1)

def timeit_timeout(command, setup='', TIMEOUT=10, default=None, err=.05):
    return timeout(Timeit, args=command, kwargs={'setup':setup},
                   TIMEOUT=TIMEOUT, default=default, err=err)

经过一些更多的讨论,我有一个基于Timeout函数的初始答案使用线程。 我仍然希望听到任何有更好想法的人,因为我还是新手。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
def timeout(func, args=None, kwargs=None, TIMEOUT=10, default=None, err=.05):
    if args is None:
        args = []
    elif hasattr(args,"__iter__") and not isinstance(args, basestring):
        args = args
    else:
        args = [args]

    kwargs = {} if kwargs is None else kwargs

    import threading
    class InterruptableThread(threading.Thread):
        def __init__(self):
            threading.Thread.__init__(self)
            self.result = None

        def run(self):
            try:
                self.result = func(*args, **kwargs)
            except:
                self.result = default

    it = InterruptableThread()
    it.start()
    it.join(TIMEOUT* (1 + err))
    if it.isAlive():
        return default
    else:
        return it.result

def timeit_timeout(command, setup='', TIMEOUT=10, default=None, err=.05):
    import timeit
    f = lambda: timeit.Timer(command, setup=setup).timeit(1)
    return timeout(f,TIMEOUT=TIMEOUT, default=default, err=err)

if __name__ == '__main__':    
    from time import sleep
    setup = 'gc.enable()
from time import sleep'

    for n in range(1,15):
        command = 'sleep({})'.format(n)
        print timeit_timeout(command, setup=setup, default=float('Inf'))