出于好奇,更希望将函数显式传递给其他函数,或让函数从内部调用函数。这是一个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])
答案 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
可以消失并执行它的通用。
但通常这种安排是不合适的。函数为高级操作提供名称并隐藏内部实现细节,因此您可以将操作视为一个单元。允许其部分操作作为函数参数从外部传入,这使得它以使用该函数接口的方式工作。
更具体(虽然有点人为)的例子可能有所帮助。假设我已经实现了代表Person
和Job
的数据类型,并且我正在编写一个函数name_and_title
,用于将某人的全名和作业标题格式化为字符串,以便插入客户端代码到电子邮件签名或信头或其他什么。显然需要Person
和Job
。它可能需要一个函数参数来让调用者决定如何格式化该人的名字:类似lambda firstname, lastname: lastname + ', ' + firstname
。但要做到这一点,就是要揭露我用一个单独的名字和姓氏代表人们的名字。如果我想更改为支持中间名,那么name_and_title
将无法包含中间名,或者我必须更改它接受的函数的类型。当我意识到有些人有4个或更多的名字并决定更改为存储名称列表时,我肯定要改变name_and_title
接受的函数类型。
所以对于你的bar
例子,我们不能说哪个更好,因为它是一个没有意义的抽象例子。这取决于对partialfun
的调用是否是bar
应该执行的任何实现细节,或者对partialfun
的调用是否是调用者知道的(并且可能需要)做别的事)。如果它是“bar
的一部分,那么它不应该是一个参数。如果它是调用者的“一部分”,那么它应该是一个参数。
值得注意的是bar
可能有巨大的个函数参数。您致电sum
,map
和operator.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
来执行函数。
所以确实没有一种确切的答案可以一直适用。这取决于您正在编写的特定代码。