在python中显式传递函数

时间:2012-08-22 22:44:57

标签: python function

出于好奇,更希望将函数显式传递给其他函数,或让函数从内部调用函数。这是一个Explicit的情况比隐含更好吗?

例如

(以下仅用于说明我的意思)

def foo(x,y):
    return 1 if x > y else 0

partialfun = functools.partial(foo, 1)

def bar(xs,ys):
    return partialfun(sum(map(operator.mul,xs,ys)))

>>> bar([1,2,3], [4,5,6])

- 或 -

def foo(x,y):
    return 1 if x > y else 0

partialfun = functools.partial(foo, 1)

def bar(fn,xs,ys):
    return fn(sum(map(operator.mul,xs,ys)))

>>> bar(partialfun, [1,2,3], [4,5,6])

3 个答案:

答案 0 :(得分:2)

在这种情况下,功能和其他任何东西之间没有任何区别。如果它是一个可能因函数的不同调用而变化的参数,则将某些内容作为参数传递。如果您调用的函数(在您的示例中为bar)始终调用相同的其他函数,则没有理由将其作为参数传递。如果你需要对它进行参数化以便你可以使用许多不同的函数(即,bar可能需要调用除partialfun之外的许多函数,并且需要知道要调用哪个函数),那么你需要传递它作为一个论点。

答案 1 :(得分:2)

一般来说,是的,但一如既往,这取决于。您在此处说明的内容称为dependency injection。通常,这是一个好主意,因为它允许将可变性与给定函数的逻辑分开。这意味着,例如,您可以非常轻松地测试此类代码。

# To test the process performed in bar(), we can "inject" a function
# which simply returns its argument
def dummy(x):
    return x

def bar(fn,xs,ys):
    return fn(sum(map(operator.mul,xs,ys)))

>>> assert bar(dummy, [1,2,3], [4,5,6]) == 32

答案 2 :(得分:2)

这在很大程度上取决于背景。

基本上,如果函数是bar的参数,那么调用者有责任知道如何实现该函数。 bar无需关心。但是,bar的文档必须描述它所需的函数。

这通常非常合适。明显的例子是map内置函数。 map实现了将函数应用于列表中的每个项目并返回结果列表的逻辑。 map本身既不知道也不关心项目是什么,或者功能对它们做了什么。 map的文档必须描述它需要一个参数的函数,map的每个调用者必须知道如何实现或找到合适的函数。但这种安排很好;它允许您传递自定义对象的列表,以及专门针对这些对象操作的函数,map可以消失并执行它的通用。

但通常这种安排是不合适的。函数为高级操作提供名称并隐藏内部实现细节,因此您可以将操作视为一个单元。允许其部分操作作为函数参数从外部传入,这使得它以使用该函数接口的方式工作。

更具体(虽然有点人为)的例子可能有所帮助。假设我已经实现了代表PersonJob的数据类型,并且我正在编写一个函数name_and_title,用于将某人的全名和作业标题格式化为字符串,以便插入客户端代码到电子邮件签名或信头或其他什么。显然需要PersonJob。它可能需要一个函数参数来让调用者决定如何格式化该人的名字:类似lambda firstname, lastname: lastname + ', ' + firstname。但要做到这一点,就是要揭露我用一个单独的名字和姓氏代表人们的名字。如果我想更改为支持中间名,那么name_and_title将无法包含中间名,或者我必须更改它接受的函数的类型。当我意识到有些人有4个或更多的名字并决定更改为存储名称列表时,我肯定要改变name_and_title接受的函数类型。

所以对于你的bar例子,我们不能说哪个更好,因为它是一个没有意义的抽象例子。这取决于对partialfun的调用是否是bar应该执行的任何实现细节,或者对partialfun的调用是否是调用者知道的(并且可能需要)做别的事)。如果它是“bar的一部分,那么它不应该是一个参数。如果它是调用者的“一部分”,那么它应该是一个参数。

值得注意的是bar可能有巨大的个函数参数。您致电summapoperator.mul,这些都可以参数化,以使bar更灵活:

def bar(fn, xs,ys, g, h, i):
    return fn(g(h(i,xs,ys))

g的输出上调用h的方式也可以被抽象出来:

def bar(fn, xs, ys, g, h, i, j):
    return fn(j(g, h(i, xs, ys)))

我们可以继续前进,直到bar根本不做任何事情,一切都由传入的函数控制,调用者也可以直接完成他们想做的事情而不是编写100个函数来执行它并将它们传递给bar来执行函数。

所以确实没有一种确切的答案可以一直适用。这取决于您正在编写的特定代码。