【问题标题】:Change decorator to implement in new module更改装饰器以在新模块中实现
【发布时间】:2018-04-12 22:18:13
【问题描述】:

我有一些类有一个字段spent_timesspent_times 是一个列表,这个类的所有方法都会写入一些信息,这对于日志记录很有价值。

另外,我有一个装饰器,它计算每个函数的执行时间并将其写入spent_times

这是我的装饰器的实现:

def timing(message):
    def wrap(function):
        def called(*args, **kwargs):
            time_start = timer()
            spent_time = round(timer() - time_start, 5)

            if not args:
                return function(*args, **kwargs), spent_time

            obj = args[0]
            if hasattr(obj, "spent_times"):
                obj.spent_times.append("{}={:.5f}".format(message, spent_time))
                return function(*args, **kwargs)
            else:
                logging.warning('Decorator allows to set spent_time attribute!')
        return called
    return wrap 

正如你在我的装饰器中看到的,有一个检查,如果调用函数具有属性 self

如果有,我可以当场在列表spent_times 中写入所需的信息,如果没有,装饰器会返回执行和函数本身所花费的时间。

我在一个模块中使用这个装饰器,第二种情况(当没有找到 self 时)属于这个模块中的一些其他函数,这些函数不属于定义了花费时间列表的类,但我在我的班级内部执行它们,所以我能够实现例如以下结构:

这是“外部”函数的声明

def calc_users(requests, priority):
     # .....

在我的课堂中,我执行它并以这种方式更新我的花费时间列表:

 response, spent_time = calc_users(requests, priority)
 self.class_obj.spent_times.append("user_calculation={:.5f}".format(spent_time))

这不是很好,但至少可以正常工作。

现在,我在不同新模块中移动了我的类的一些功能,我想使用相同的装饰器时间。

有人可以帮我在新模块中实现这种计时实现吗?我不知道,我现在可以做什么来更新我的spent_times 列表。

这两个模块将同时工作,我无法创建类的对象并将其作为参数传递给新模块,因为(据我了解)将有两个对象并且花费时间不会正确更新.

也许有办法以某种方式传递对spent_times 的引用,但我不想在新模块中更改我的函数的参数,因为我认为在这种情况下,共享责任原则将被打破(装饰者负责用于记录,函数用于其操作)。

那么如何改进装饰器或如何将spent_times 列表传递给新模块?

任何帮助将不胜感激!

附:

也许将spent_times 设为全局变量? (在最坏的情况下)

【问题讨论】:

    标签: python python-2.7 decorator


    【解决方案1】:

    全局列表看起来不错,但您也可以使用类并通过在实例化后删除类来创建单例。这可以防止创建另一个实例:

    # mymodule.py
    
    from timeit import default_timer as timer
    
    class Timing(object):
        def __init__(self):
            self.spent_times = []
    
        def __call__(self, message):
            def wrap(function):
                def called(*args, **kwargs):
                    time_start = timer()
                    spent_time = round(timer() - time_start, 5)
                    self.spent_times.append("{}={:.5f}".format(message, spent_time))
                    return function(*args, **kwargs)
                return called
            return wrap 
    
    timing = Timing()
    del Timing # prevent another instance
    

    现在导入另一个模块:

    from mymodule import timing
    
    @timing('Hello')
    def add(a, b):
        return a + b
    

    特殊方法__call__ 使类的实例表现得像一个函数,即它可以用() 调用。 优点是您可以使用self.attr 而不是全局变量。

    实例化后删除类会阻止创建另一个实例。这称为单例。现在,无论您使用 timing 作为装饰器的频率如何,您的所有时间都会在同一个列表中结束。

    【讨论】:

    • 维伦丹克! Das ist aber nicht so leich zu verstehen..(谢谢!虽然看起来不那么容易理解)
    • 添加了更多解释。
    • 感谢您更详细的解释!我在这里看到另一个任务,现在我必须确定在 mymodule.py 中删除时间。我怎样才能找出必须删除时间的时刻..?
    • 您不必删除课程Timing。仅当您想确保只有一个具有测量时间的列表时。
    猜你喜欢
    • 2012-02-16
    • 2011-03-08
    • 2019-03-25
    • 1970-01-01
    • 2021-04-07
    • 2018-03-10
    • 2018-12-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多