2

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

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

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])
4

3 回答 3

2

这在很大程度上取决于上下文。

基本上,如果函数是 的参数bar,那么调用者有责任知道如何实现该函数。bar不必在意。但因此,bar的文档必须描述它需要什么的功能。

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

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

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

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

值得注意的是,它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给执行功能。

因此,实际上并没有一个始终适用的明确答案。这取决于您正在编写的特定代码。

于 2012-08-23T02:48:49.890 回答
2

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

于 2012-08-22T22:52:53.197 回答
2

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

# 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
于 2012-08-22T22:53:02.467 回答