2015-11-08 60 views
2

自从almost forever以来,Python已经有了lambda表达式。然而,scipy和一些其他库遵循different paradigm,同时接受作为参数的函数,例如为什么`scipy`接受单独的`args`作为目标函数?

def minimize(fun, x0, args=(), [...]): 
''' 
where: 
    args : tuple, optional 
      Extra arguments passed to the objective function [...] 
''' 
... 

,他们接受额外的参数传递到fun作为一个单独的列表/元组args而不是鼓励lambda功能的使用。这似乎是在代码中可以避免的一个额外的理由。

这是为什么? lambda函数是否较慢?还是仅仅遵循其他语言的约定,如R(可能会提出相同的问题并指向S)?

如果我正在设计一个新的API,在尊重这个选择方面有优势吗?

+1

这几乎是一个更广泛的语言设计问题。 – zxq9

回答

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) 

特拉维斯头是一个非常重要的numpy原开发商。注意早年的变化。我假设这个包装是在scipy安装在github存储库之前很久才添加的,但是你肯定可以检查。

描述包装功能如何解释他为什么选择它。这可能是因为他在FORTRAN和C包之后对API进行了建模。其中一些scipy优化,ode和插值函数最终使用编译代码。


我发现改变了这种包装函数的语法2013拉请求:

- 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作为列表或单个项目,而不是元组。有SO问题,人们使用args=(x)而不是args=(x,)。在issue中提到的替代方案是functools.partial

+0

感谢您关注此事。所以看起来,API设计可能是靠保持原始Fortran API的动力。同时考虑到'args'这个参数的各种问题,看起来对于为新的API做出这种设计选择没有好处? –

+1

它仍然会包装它以获得'ncalls'属性。但是,如果“partial”或“lambda”适合你,我认为没有理由不使用它们。 Guido想从Python3中移除'lambda',但其他人占上风。 http://www.python-course.eu/python3_lambda.php – hpaulj

1

这其中很大一部分是因为Python具有“半个lambda”,而不是像lisp或Erlang那样真正成熟的lambdas。

Python lambda仅限于单个表达式*,不能包含某种内置的副作用语句,并且不能将其扩展为多行而不将它们包含在parens中。命名函数可以自由传递,可以做任何你想象的事情。语义空白的语言没有什么动机来调整其余的语法来适应任意复杂的lambda表达式。

最终,在某些情况下,Python lambda可以很方便,但它们根本不是任意强大的设计

(这可以通过括号和产品线延伸的方式来写到位大毛lambda表达式,但非常凌乱和unpythonic。这些天,即使在二郎有一种趋势,经过多年获得与lambda表达式很疯狂的只使用lambda定义在调用外部定义的函数时使用lambda定义,以这种方式使用lamdbas(或lisp)lambda甜蜜点,并在出现问题时提供更好的跟踪。)

所以折中方法与我们在Python其他地方找到的相同:显式定义函数,然后将它们传递给你,如果你愿意的话。至于为什么scipy使用这种特定的参数传递技术,我想这是因为他们想让你定义和使用任意函数或任意参数,这是很容易处理命名函数而不是大疯狂其他Python程序员会憎恨你定义的就地lambda表达式。

相关:

[*我曾错误地写了 “一个参数” 之前,罗伯特·克恩纠正我。]

+2

Python lambda当然不限于一个参数。它们可以包括任何功能,副作用或否;如果这就是你所指的那么它们不能包含Python 2的'print'语句,但这不是一个函数。 –

+0

@RobertKern事实上,它们仅限于一个表达式,而不是单一的参数。是的,py2 print是一个声明,而不是一个函数。但是,尽管如此,它们却一直处于跛足状态 - 但出于一个经过仔细考虑的原因,这是一个坚实设计的标志。 – zxq9

+0

这个库可能仍然把定义一个新的函数的责任放在用户身上,并且根本不接受任何额外的参数。事实上,要记住GVR本身并不是一个lambda的粉丝,我会创建一个新的'def'函数来传入,我认为这比传递额外的'args'更清晰。另请参阅hpaulj的回答以及'args'的'tuple'形式引起的问题。在那种情况下,或许一个新的API不会因保留额外的'args'而受益。你怎么看? –

1

是的,lambda功能可能会变慢,特别是如果内部函数是C扩展函数,通常是这种情况。该函数在最小化器内部相对紧密的循环中被调用,因此削减额外Python函数调用的开销可以提供帮助。

functools.partial()lambda功能更好的选择,并且可以更清晰或大于传递args=minimize()更方便,但是当这些API正在许多年前它不存在。

+0

我找不到一个确定lambda函数开销的权威源。与跳过Python-C屏障的现有开销相比,开销是否可以忽略不计?我怀疑lambda表达式更容易针对JIT进行优化(虽然我可能在这里是错误的)。你能否提供一个资料来源?谢谢! –

+1

如果lambda开销是使用'args'的原因--- lambda开销很小(这里〜40ns),这与在优化例程或目标函数中完成的其他处理相比可能很小本身。 –

+0

现在测量可能不会反映相对性能c.a.最终这个API选择来源于(Travis Oliphant的multilab被scipy采纳,然后在更新的'minimize()'中保持一致性)。 @musically_ut,它取决于函数的内容,但由于主要输入通常是numpy数组,所以通常转换成本很低。在创建API时,JITted优化器不存在,所以当然不是那个因素。 –

相关问题