【问题标题】:How much overhead do decorators add to Python function calls装饰器给 Python 函数调用增加了多少开销
【发布时间】:2011-04-02 12:30:27
【问题描述】:

我一直在为我的 pylons 应用程序使用计时装饰器,以便为特定功能提供即时计时信息。我通过创建一个装饰器并简单地将它附加到我想要计时的控制器中的任何函数来完成此操作。

但有人指出,装饰器可能会为调用增加相当多的开销,并且它们的运行速度比未装饰的函数慢 2-3 倍。

首先,我希望执行一个修饰函数比未修饰函数花费更长的时间,但我希望开销在千分之一秒内并且与 SQL 插入调用相比可以忽略不计。装饰器本身使用 time.time() 和一些非常简单的聚合进行简单的简单计时计算。

装饰器是否会为系统增加大量开销?我找不到任何东西来支持它。

【问题讨论】:

  • 装饰本身的开销是可衡量的,但远不及 2-3 倍。这没有考虑装饰行为,这可能会非常耗时,具体取决于您在做什么(数据库更新、从月球反弹的信号等)。
  • +1 用于从月球反射信号
  • 同样,但保留 +2 以供将来评论:“同步月球信号弹跳”

标签: python performance decorator


【解决方案1】:

使用装饰器增加的开销应该只是一个额外的函数调用。

装饰器所做的工作不是开销的一部分,因为您的替代方法是将等效代码添加到装饰对象。

因此,decorate 函数可能需要两倍的时间来运行,但这是因为 decorator 正在做一些重要的工作,而这些工作与未装饰的函数所花费的时间大致相同。

【讨论】:

    【解决方案2】:

    重要的是要知道装饰器有一个简单的效果:

    @decorator
    def f():
        …
    

    只是

    的语法糖
    def f():
        …
    f = decorator(f)
    

    因此,如果装饰器不做任何事情,当 调用 装饰函数时您不会有任何开销(尽管调用 decorator(f) 确实需要一点时间),就像在

    decorator = lambda func: func
    @decorator
    def f():
        …
    

    如果装饰器做了任何事情,你只会得到装饰器所涉及的任何时间开销。这通常包括一个额外的函数调用(装饰函数的调用),如

    def decorator(func):
        def decorated_func():
            print "Before calling function", func  # Some overhead (but that's normal)
            func()  # This will be a second function call, after the call to decorated_func()
        return decorated_func
    

    因此,装饰函数本身并不会为您想要做的事情增加太多开销:原则上您可以删除的唯一明显开销是不在装饰函数中调用func(),而是复制其完整代码,但代码的易读性会受到影响(易读性和灵活性是装饰器首先存在的一些原因)。

    【讨论】:

    • -1 Python 中的函数调用开销并非微不足道。并且建议将func() 的代码复制到装饰器中首先会破坏装饰器的全部意义。如果有的话,我更愿意将装饰器的代码复制到func() 中,但同样,如果我们这样做,使用装饰器的简单性就会被破坏,func() 的代码会被装饰行为污染。你真的不能说“如果装饰器不做任何事情,你就没有任何开销”。装饰器对于代码跟踪等很有用,但它们的代价是在运行时增加了一个函数调用。
    • @Paul:确实,函数调用开销在 Python 中相对显着(从未说过!)。我同意将func() 的代码放在装饰器中会破坏装饰器的全部意义(如我所写)的事实。因此我们完全同意!我只是给出关键点,让原始发布者准确了解装饰器的开销来自何处。
    • 我的主要问题是你的陈述“如果装饰器不做任何事情,你就没有任何开销”,这听起来像是你忽略了额外的函数调用。我去删除我的反对票,但除非你对你的答案进行一些编辑,否则我不会让我这样做。
    • @Paul:谢谢,我明白了。为了解释您的评论,我使答案更准确。
    【解决方案3】:

    装饰器是否会为系统增加大量开销?我找不到任何东西来支持它。

    它们几乎不会增加可衡量的开销。零。

    请务必注意,装饰器运行一次以创建装饰功能。 一次。

    装饰函数有两个部分。

    1. 添加了任何装饰。这不是开销。

    2. 加上原来的功能。这不是开销。

    根本没有真正的开销。您可能 - 稍加注意 - 能够测量一个额外的函数调用和返回的开销作为修饰函数的一部分,但这几乎是无法测量的小。而且它可能远不如不使用装饰的替代设计。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-05-07
      • 1970-01-01
      • 2017-12-11
      • 1970-01-01
      • 2017-01-15
      • 2017-08-01
      • 1970-01-01
      相关资源
      最近更新 更多