自almost forever以来,Python已经有了lambda表达式。但是,scipy
和其他一些库遵循different paradigm,同时接受函数作为参数,例如:
def minimize(fun, x0, args=(), [...]):
'''
where:
args : tuple, optional
Extra arguments passed to the objective function [...]
'''
...
即,他们接受额外的参数作为单独的列表/元组fun
传递给args
,而不是鼓励使用lambda
函数。这似乎是在代码中可以避免的另一个理由。
为什么会这样? lambda函数是否较慢?或者它只是遵循其他语言的惯例,例如R
(可能会问同一个问题并指向S
)?
如果我正在设计一个新的API,那么在选择这个选择方面是否有优势?
答案 0 :(得分:1)
这很大一部分原因是因为Python有半个lambda"而不是真正完整的lambdas,就像你可能习惯于来自lisp或Erlang。
Python lambdas仅限于单个表达式*,不能包含某个内置的副作用语句,并且不能扩展为多行而不将它们包含在parens中。命名函数可以自由传递,可以做任何你想象的事情。在具有语义空白的语言中,很少有动机来调整语法的其余部分以适应任意复杂度的lambda。
最终,Python lambdas在某些情况下可能很方便,但它们根本不是设计任意强大的。
( 可能通过parens和line扩展来编写大毛羔羊,但非常混乱和unpythonic。这些天甚至在Erlang中有一种趋势,经过多年的漂亮疯狂的lambdas到位,只使用lambda定义将一些局部状态包含在对外部定义函数的调用中。以这种方式使用lamdbas语言(或lisp)lambda最佳点,并在崩溃时提供更好的跟踪。 )
所以妥协与我们在Python其他地方找到的一样:明确定义函数,然后根据需要传递它们。
至于为什么scipy使用这种特殊的参数传递技术,我想这是因为他们想让你定义和使用任意函数或任意arity,这对于命名更容易 功能比其他Python程序员讨厌你定义的大疯狂就地lambdas。
相关:
[*我错误地写了#34;单个参数"之前,罗伯特克恩纠正了我。]
答案 1 :(得分:1)
是的,lambda
函数可能更慢,特别是如果内部函数是C扩展函数,通常就是这种情况。该函数在最小化器内部的相对紧密的循环内调用,因此削减额外的Python函数调用的开销可以提供帮助。
functools.partial()
是比lambda
函数更好的替代方法,可能比将args=
传递给minimize()
更清晰或更方便,但当它们不存在时,它们并不存在API是多年前制作的。
答案 2 :(得分:1)
当我查看optimize.minimize
代码时,我发现它将任务传递给_minimize_neldermead
等函数。这些调用反过来如下:
fcalls, func = wrap_function(func, args)
<more setup>
... = func(x0)
... = func(y)
(来自scipy/optimize/optimize.py
):
def wrap_function(function, args):
ncalls = [0]
if function is None:
return ncalls, None
def function_wrapper(*wrapper_args):
ncalls[0] += 1
return function(*(wrapper_args + args))
return ncalls, function_wrapper
所以args
通过将它们连接到变量(wrapper_args
)并将元组传递给你的函数来处理。这是一个简单,直接的包装机制。
此.py
文件的标头为
# optimize.py module by Travis E. Oliphant
...
# A collection of optimization algorithms. Version 0.5
# CHANGES
# Added fminbound (July 2001)
Travis是numpy
大部分的原创开发人员。请注意改变的早期年份。我假设在github存储库上安装scipy之前很久就添加了这个包装器,但你当然可以检查。
描述包装功能如何解释他选择它的原因。可能是他在FORTRAN和C包之后对API进行建模。其中一些scipy
优化,ode和插值函数最终使用已编译的代码。
我发现了2013年的pull请求改变了这个包装函数的语法:
- def function_wrapper(x):
+ def function_wrapper(*wrapper_args):
ncalls[0] += 1
- return function(x, *args)
+ return function(*(wrapper_args + args))
https://github.com/scipy/scipy/commit/cf3adca80e371fd19a34b398d2f1ed0e19f0cbdc
https://github.com/scipy/scipy/issues/3785
显然有些人在将args
定义为列表或单个项目而不是元组时遇到了问题。有些问题是人们使用args=(x)
而不是args=(x,)
。此issue
中提及的替代方法是functools.partial
。