在python中显式传递函数

在python中显式传递函数,python,function,Python,Function,出于好奇,更可取的做法是显式地将函数传递给其他函数,或者让函数从内部调用函数。这是显式优于隐式的情况吗 例如(以下只是为了说明我的意思) --或-- 在这种情况下,函数和其他任何东西之间实际上没有任何区别。如果某个参数可能因函数的不同调用而不同,则可以将其作为参数传递。如果您正在调用的函数(bar,在您的示例中)总是调用相同的其他函数,则没有理由将其作为参数传递。如果需要对其进行参数化,以便可以使用许多不同的函数(例如,bar可能需要调用除partialfun之外的许多函数,并且需要知道要调用哪

出于好奇,更可取的做法是显式地将函数传递给其他函数,或者让函数从内部调用函数。这是显式优于隐式的情况吗

例如(以下只是为了说明我的意思)

--或--


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

通常,是的,但始终取决于。您在这里演示的内容称为。一般来说,这是一个好主意,因为它允许从给定函数的逻辑中分离可变性。例如,这意味着您将非常容易地测试此类代码

# 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

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

基本上,如果函数是
bar
的参数,则调用方有责任知道如何实现该函数<代码>条形码不必在意。但是因此,
bar
的文档必须描述它需要什么样的功能

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

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

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

因此,对于您的
bar
示例,我们不能说哪个更好,因为它是一个抽象示例,没有任何意义。这取决于对
partialfun
的调用是否是
bar
应该执行的任何操作的实现细节,或者对
partialfun
的调用是否是调用方知道的(并且可能希望执行其他操作)。如果它是“
条的一部分”
,那么它不应该是一个参数。如果它是调用者的“一部分”,那么它应该是一个参数

值得注意的是,
bar
可能有大量的函数参数。您可以调用
sum
map
operator.mul
,这些都可以参数化,以使
条形图更灵活:

def bar(fn, xs,ys, g, h, i):
    return fn(g(h(i,xs,ys))
h
的输出调用
g
的方式也可以抽象:

def bar(fn, xs, ys, g, h, i, j):
    return fn(j(g, h(i, xs, ys)))
我们可以继续下去,直到
bar
什么都不做,一切都由传入的函数控制,调用者也可以直接做他们想做的事情,而不是编写100个函数来做,然后将它们传递给
bar
来执行函数


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

这是一个很好的答案-非常彻底。尽管如此,如果你提到单元测试,我还是会喜欢的o) +1-感谢您抽出时间来完成此操作!我已经养成了使用partial传递函数和构建新函数的习惯,但我想我可能在不需要它的时候就这样做了。你的句子“如果它是”条的一部分,那么它不应该是一个参数。如果它是调用者的“一部分”,那么它应该是一个参数。太好了,谢谢你提供依赖注入的链接。它肯定澄清了一些事情(我甚至没有意识到我在问的事情)
def bar(fn, xs,ys, g, h, i):
    return fn(g(h(i,xs,ys))
def bar(fn, xs, ys, g, h, i, j):
    return fn(j(g, h(i, xs, ys)))