Pythonic方法参数化一个函数(没有lambda)

时间:2017-06-12 18:46:47

标签: python function lambda

这是一个关于编程风格的问题:参数化函数的最“Pythonic”方法是什么(即使是正确的单词呢?)

假设我有一个函数(例如ODE求解器)接受两个参数的另一个函数(例如ODE本身)作为参数。

def solver(fun):
   # Implementation goes here
   # fun is a function handle that accepts two args e.g. fun(t,y)

但是,我希望传递给solver的函数由第三个值

参数化
def myFun(t,y,A):
   # Implementation goes here

我一直在使用lambda函数处理这种情况,如下所示:

A = 4
solution = solver(lambda t,y:myFun(t,y,A))

我最近在网上看到一些帖子告诉我要避免lambda像瘟疫一样,并且Guido自己后悔允许这个功能。如果lambda确实很糟糕,那么实施上述内容的“Pythonic”方法有什么用?没有lambda我遇到了无法访问全局命名空间的问题,即我想这样做:

A = 4
def myFunNotLambda(t,y):
    return myFun(t,y,A)
solution = solver(myFunNotLambda)

但这样做的唯一方法似乎是A全局,这肯定比使用lambda

更糟糕

3 个答案:

答案 0 :(得分:5)

您可以使用functools.partial,例如:

from functools import partial

A = 4
solution = solver(partial(myFun,A=A))

partial(..)构造给定一个函数(此处为myFunc)另一个函数,其中A参数现在具有A的默认值。

答案 1 :(得分:3)

建议“避免lambda像瘟疫一样”是一种严重的夸张,最糟糕的是纯粹的FUD - 在Python的标准库中gre [{1}}显示数千匹配。

虽然Guido确实expressed later concern关于lambda主导的编码风格,但这是在(过度)使用像lambdamap这样的功能结构的上下文中,它们表达得更好在Python中使用列表推导和生成器表达式。

然而,当谈到创建所需的临时函数时,使用reduce并没有错;相反,它是这项工作的最佳工具。如果您仍想避免使用lambda关键字,则可以使用嵌套的lambda

def

如其他答案所示,也可以使用def my_particular_fun(t, y): return my_fun(t, y, 4) solution = solver(my_particular_fun) 来模拟lambda,但需要将第三个参数的名称修改为functools.partial

答案 2 :(得分:1)

执行此操作的一种非常有效的方法是使用functools.partial,但正如已经指出的那样,partial只允许您“冻结”最终的args。如果您还需要其他内容,可以使用closure轻松实现自己的版本。

这种方法实际上比使用partial对象更有效,因为当你调用partial时,它仍然必须调用你传入的原始函数,所以每次调用{ {1}}导致两次调用,Python函数/方法调用相对较慢。如果您感到好奇,请查看the Python source code for functools.partial

在这个例子中,我将partial作为函数的第二个(伪)参数,因为A很好地处理了它是最后一个arg的情况。

partial

<强>输出

def my_partial(param):
    A = param
    print('Creating two_arg_func with A ==', A)
    def two_arg_func(t, y):
        # Do something with all the args
        return 'args', t, A, y
    return two_arg_func

def test(f):
    for u in range(10, 50, 10):
        print(f(u, u + 5))

test(my_partial(7))

我们在Creating two_arg_func with A == 7 ('args', 10, 7, 15) ('args', 20, 7, 25) ('args', 30, 7, 35) ('args', 40, 7, 45) 中确实不需要param,我们可以使用传入的arg,因为它是my_partial的本地:

my_partial

从您的评论中,我现在明白您希望能够改变def my_partial(A): print('Creating two_arg_func with A ==', A) def two_arg_func(t, y): return 'args', t, A, y return two_arg_func 。当然,您可以通过再次致电Apartial来实现这一目标,但如果您想要修改my_partial,那么效率会不高。

您的评论表明您想在全局上下文中修改A,因此您也可以使用全局。您不需要将修改A的代码实际放入全局上下文中,您可以将其包装在函数中,但当然需要使用A修改global的函数中的指令。但 not 在任何只读取A的值的函数中都需要global指令。

这是一个简短的演示。

A

<强>输出

def two_arg_func(t, y):
    # Do something with the args and with A
    return 'args', t, A, y

def solve(f):
    for u in range(10, 40, 10):
        print('SOLVER', f(u, u + 5))

def test(f):
    global A
    for A in range(7, 10):
        print(A)
        solve(f)

test(two_arg_func)

然而,之前的解决方案有些不尽如人意,因为您的问题的主要内容是如何在没有使用全局的情况下执行此操作。所以这里的代码略有不同。我们不将7 SOLVER ('args', 10, 7, 15) SOLVER ('args', 20, 7, 25) SOLVER ('args', 30, 7, 35) 8 SOLVER ('args', 10, 8, 15) SOLVER ('args', 20, 8, 25) SOLVER ('args', 30, 8, 35) 9 SOLVER ('args', 10, 9, 15) SOLVER ('args', 20, 9, 25) SOLVER ('args', 30, 9, 35) 放在全局命名空间中,而是将其作为函数属性附加到A。我们可以这样做,因为Python函数是一个对象,它已经拥有一大堆属性;您可能熟悉的两个是two_arg_func__name__。无论如何,这是新代码,它打印的输出与先前版本相同。

__doc__