装饰器给Python函数调用增加了多少开销

23 投票
4 回答
6951 浏览
提问于 2025-04-16 03:10

我最近在我的pylons应用中玩一个计时装饰器,目的是为了实时获取特定函数的执行时间。我是通过创建一个装饰器,然后简单地把它附加到我想要计时的控制器中的任何函数上来实现的。

不过,有人指出,装饰器可能会给调用增加不少负担,而且它们的执行速度比没有装饰器的函数慢2到3倍。

首先,我本来就预期,执行一个带装饰器的函数会比不带装饰器的函数稍微慢一点,但我认为这种额外的时间应该在千分之一秒之内,和SQL插入操作相比几乎可以忽略不计。这个装饰器本身只是用time.time()做一些简单的计时计算和非常简单的数据汇总。

那么,装饰器真的会给系统带来显著的负担吗?我找不到任何支持这个说法的证据。

4 个回答

2

装饰器会给系统带来很大的负担吗?我找不到任何支持这个说法的资料。

其实,它们几乎不会增加任何可测量的负担。可以说是零。

需要注意的是,装饰器只会运行一次,用来创建被装饰的函数。就只运行一次。

被装饰的函数有两个部分。

  1. 添加的装饰部分。这部分不算负担。

  2. 加上原来的函数。这部分也不算负担。

实际上没有什么真正的负担。如果你小心一点,可能会测量到一次额外的函数调用和返回,这算是装饰函数的一部分,但这个负担几乎是微乎其微的。而且,这个负担可能远远小于不使用装饰器的其他设计方案。

4

重要的是要知道,装饰器的作用很简单:

@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(),而是直接复制它的完整代码,但这样会影响代码的可读性(可读性和灵活性正是装饰器存在的原因之一)。

15

使用装饰器带来的额外开销其实就只是多了一次函数调用。

装饰器所做的工作并不算在这额外开销里,因为如果不使用装饰器,你需要把相同的代码直接加到被装饰的对象里。

所以,装饰函数的运行时间可能会是普通函数的两倍,但这只是因为装饰器在做一些重要的工作,这些工作大概和没有装饰的函数运行所需的时间差不多。

撰写回答