在Python中部分应用的高性能方法?

Lud*_*igH 5 python lambda partial-application

我正在寻找一种在 python 中部分应用函数的方法,该方法易于理解、可读、可重用,并且尽可能少地容易出现编码错误。最重要的是,我希望该样式尽可能具有高性能 - 堆栈上的帧越少越好,部分应用的函数的内存占用也越少越好。我考虑了 4 种风格并写了下面的例子:

import functools

def multiplier(m):
    def inner(x):
        return m * x

    return inner

def divide(n,d):
    return n/d

def divider(d):
    return functools.partial(divide,d=d)

times2 = multiplier(2)
print(times2(3))  # 6

by2 = divider(2)
print(by2(6)) # 3.0

by3 = functools.partial(divide,d=3)
print(by3(9)) # 3.0

by4 = lambda n: divide(n,4)
print(by4(12)) # 3.0
Run Code Online (Sandbox Code Playgroud)

我对他们的分析是:

times2是一个嵌套的东西。我猜想 python 用边界做了一个闭包m,一切都很好。该代码是可读的(我认为)并且易于理解。没有外部库。这就是我今天使用的风格。

by2有一个明确的命名函数,这对用户来说很简单。它使用 functools,因此它为您提供了额外的导入。我在某种程度上喜欢这种风格,因为它是透明的,如果我愿意的话,我可以选择divide其他方式使用。将此与inner不可到达的进行对比。

by3就像by2,但是迫使代码的读者感到舒服,functools.partial因为他们就在眼前。我不太喜欢的是 PyCharm 无法为我的工具提示提供参数应该functools.partial是什么,因为它们实际上是by3. divide每次定义一些新的部分应用程序时,我都必须知道自己的签名。

by4打字很简单,因为我可以自动完成。它不需要导入functools. 我认为它看起来不符合Python风格。另外,我总是对 python 中 lambda 工作的变量/闭包的范围感到不舒服。永远不知道它的行为如何......

风格之间的逻辑差异是什么?这对内存和 CPU 有何影响?

Joh*_*man 3

第一种方法似乎是最有效的。我调整了你的代码,以便所有 4 个函数计算完全相同的数学函数:

import functools,timeit

def multiplier(m):
    def inner(x):
        return m * x

    return inner

def mult(x,m):
    return m*x

def multer(m):
    return functools.partial(mult,m=m)

f1 = multiplier(2)
f2 = multer(2)
f3 = functools.partial(mult,m=2)
f4 = lambda x: mult(x,2)

print(timeit.timeit('f1(10)',setup = 'from __main__ import f1'))
print(timeit.timeit('f2(10)',setup = 'from __main__ import f2'))
print(timeit.timeit('f3(10)',setup = 'from __main__ import f3'))
print(timeit.timeit('f4(10)',setup = 'from __main__ import f4'))
Run Code Online (Sandbox Code Playgroud)

典型输出(在我的机器上):

0.08207898699999999
0.19439769299999998
0.20093803199999993
0.1442435820000001
Run Code Online (Sandbox Code Playgroud)

这两种functools.partial方法是相同的(因为其中一种只是另一种的包装),第一种方法的速度是另一种方法的两倍,最后一种方法介于两者之间(但更接近第一种)。与直接的闭包相比,使用它有明显的开销functools。由于闭包方法可以说也更具可读性(并且比 lambda 更灵活,而 lambda 不能很好地扩展到更复杂的函数),因此我会选择它。