关键字参数性能(Python)

3

我正在尝试通过使用timeit测试各种函数来优化一些Python代码。

我发现,变量是关键字参数还是在函数内部会影响速度。

也就是说:

def test_function(A = value()):
    #rest of function....

返回的结果与以下内容不同:
def test_function():
    A = value()
    #rest of function ...

我本以为它们会有非常相似的结果 - 我猜我在这里理解/遗漏了什么...(测试也进行了10,000次循环)

确保您理解执行 value() 的道德准则。如果这是一些随机 id 生成器,您将会遇到麻烦。那个 id 将永远默认存在。因此请确保通过调用 value() 来检查您的应用程序是否正常。 - User007
在CPython中有一个fast_function的速度优化,用于处理仅限位置参数调用,如果函数对象缺少默认参数或单元格变量/自由变量设置,则使用PyEval_EvalCodeEx。否则,它将使用PyEval_EvalCodeEx。我的测试表明,在没有默认值和有默认值的情况下,使用255个参数可以提高约5-7%的性能。但更重要的是,使用255个关键字参数的速度要慢12-13倍,因为使用关键字进行调用需要更多的工作来设置和评估。 - Eryk Sun
话虽如此,大多数函数的代码评估将完全超过调用开销。 - Eryk Sun
3个回答

11

关键字参数在函数定义时只计算一次。因此,在您的第一个示例中,无论您调用测试函数多少次,value()都只被调用一次。如果value()花费较多时间,这就解释了两个版本之间运行时间差异的原因。


好的,谢谢 - 这就是我所缺少的东西!非常感谢。 - djmac

4

有人讨论为什么这种方法不是确定方法有效性的最佳方法,但如果你使用dis来检查函数的字节码,你会发现它们的结构不同,即t1在定义时计算其默认参数,因此不需要在后续函数调用中重新定义:

>>> import dis
>>> def t1(A=1):
...   pass
>>> def t2():
....  A=1
>>> dis.dis(t1)
  2           0 LOAD_CONST               0 (None)
              3 RETURN_VALUE        
>>> dis.dis(t2)
  2           0 LOAD_CONST               1 (1)
              3 STORE_FAST               0 (A)
              6 LOAD_CONST               0 (None)
              9 RETURN_VALUE        

1
谢谢 - 我以前没接触过 dis 模块 - 看起来非常有用。 - djmac
@djmac绝对有时候会派上用场。顺便问一句,好问题! - RocketDonkey

4

你的两个函数没有理由期望执行相同的任务,更别说具有相同的性能特征了。

def test_function(A = value()):
    #rest of function....

这个函数没有所谓的"关键字参数",它只有一个带有默认值的参数。任何函数的任何参数(除了一些顽固的内建函数)都可以通过关键字或位置传递,但是参数本身并不是"关键字参数"。

关键字参数和默认值之间唯一的联系是:当您有多个带有默认值的参数时,如果想为后面的参数提供明确的值而接受前面的参数的默认值,则必须通过关键字传递后面的参数。

这两个函数之间的巨大区别在于,当您为 A 声明一个默认值时,它是一个默认,而不是一些代码,如果未提供明确值,则每次都会重新生成该值。当您这样说时:

def test_function(A = value()):
    #rest of function....

你正在为A设置一个默认的。和任何其他上下文一样,当你在需要提供一个值给Python时,如果你提供一个复杂表达式,Python会计算这个表达式并使用结果值。因此,在函数定义时设置A的默认值,它将被设置为那个时间value()返回的任何值。然后,这一个单独的值就是A的默认值。
def test_function():
    A = value()
    #rest of function ...

在这个函数中,每次调用该函数都会评估value()。因此,如果value()很耗费时间,那么这个版本将比第一个版本需要更长的时间。但是,如果value()返回的是一个您稍后会改变的对象,则默认参数版本将始终使用单个对象,无论在调用函数时处于什么状态,而第二个版本将每次构造一个新的value对象。哪个版本应该使用取决于您希望程序具有的语义。

感谢您的认真回复!我完全不知道。干杯。 - djmac
在Python中,确实存在关键字参数(keyword argument),即使是在Python 2中也是如此。而在Python 3中,还有关键字-仅限(keyword-only)参数。 - agf
@agf,你提供的教程正是我所理解的关键字参数。一个函数只有形式参数(和可选的**kwargs,所以你可以认为它从**kwargs中监听到的任何特定名称都是“关键字参数”)。在调用时,参数可以通过位置或关键字传递;这不是函数形式参数的属性。的确,我省略了提及**kwargs(或Python3中的仅限关键字参数)以简化信息。我想表达的核心观点是,“关键字”与“默认值”无关。 - Ben

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接