【问题标题】:Unexpected decorator/generator behavior意外的装饰器/生成器行为
【发布时间】:2021-02-26 18:42:55
【问题描述】:

我创建了一个计时器装饰器,但如果装饰函数是生成器,它就不起作用。

import numpy as np
from time import time
from collections import Counter


def timer(f):
    def inner(*args, **kwargs):
        start = time()
        try:
            res = f(*args, **kwargs)
        except Exception as e:
            end = time()
            timer.counter.update({f'{f.__module__}.{f.__name__}': end - start})
            raise e
        end = time()
        timer.counter.update({f'{f.__module__}.{f.__name__}': end - start})
        return res
    return inner
timer.counter = Counter()


class AA:
    @timer
    def __init__(self):
        a = np.array(range(1_000_000))

    @timer
    def __iter__(self):
        a = np.array(range(1_000_000))
        yield 'a'

    @timer
    def normal_fun(self):
        a = np.array(range(1_000_000))

    @timer
    def fun_with_yield(self):
        a = np.array(range(1_000_000))
        yield 'a'


a = AA()
for i in a:
    pass
a.normal_fun()
a.fun_with_yield()
print(timer.counter)

输出:

Counter({'ma​​in.init': 0.10380005836486816, 'ma​​in.normal_fun': 0.10372400283813477, 'ma​​in.iter': 0.0, 'ma​​in.fun_with_yield': 0.0})

为什么生成器函数的时间等于 0.0,我该如何解决?

【问题讨论】:

  • 调用生成器函数实际上并不执行其中的任何代码——它只是创建了一个迭代器对象,这很容易花费比time.time() 注册的时间更少的时间。您必须遍历返回的对象才能发生任何事情。
  • 能否请您发布print(time.get_clock_info('time').resolution)的输出?
  • 谢谢。我怀疑是这样的。所以要解决这个问题,我必须检查f 类型,如果它是生成器,请使用yield 而不是return 并分别计算每次迭代,对吗?或者,还有更好的方法?为了清楚起见,我不想测量在 for 循环中花费的时间。

标签: python generator python-decorators


【解决方案1】:

最后,我通过为生成器创建另一个装饰器达到了预期的效果。装饰器本身就是一个生成器,它计算每次迭代所花费的时间。

def generator_timer(f):
    def inner(*args, **kwargs):
        start = time()
        try:
            res = f(*args, **kwargs)
            for i in res:
                end = time()
                timer.counter.update({f'{f.__module__}.{f.__name__}': end - start})
                yield i
                start = time()
        except Exception as e:
            end = time()
            timer.counter.update({f'{f.__module__}.{f.__name__}': end - start})
            raise e
        end = time()
        timer.counter.update({f'{f.__module__}.{f.__name__}': end - start})
    return inner


class AA:
    @timer
    def __init__(self):
        a = np.array(range(10_000_000))

    @generator_timer
    def __iter__(self):
        a = np.array(range(10_000_000))
        yield 'a'
        yield 'b'

    @timer
    def normal_fun(self):
        a = np.array(range(10_000_000))

    @generator_timer
    def fun_with_yield(self):
        a = np.array(range(10_000_000))
        yield a

Counter({'ma​​in.init': 1.0399727821350098, 'ma​​in.iter': 1.0183088779449463, 'ma​​in.fun_with_yield':1.0168907642364502,'ma​​in.normal_fun':1.0156745910644531})

【讨论】:

    猜你喜欢
    • 2021-08-17
    • 1970-01-01
    • 2022-01-09
    • 2017-04-02
    • 2020-10-25
    • 2014-12-26
    • 1970-01-01
    • 2021-08-28
    • 2023-03-09
    相关资源
    最近更新 更多