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 有何影响?
第一种方法似乎是最有效的。我调整了你的代码,以便所有 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 不能很好地扩展到更复杂的函数),因此我会选择它。