(免责声明:不是 Python child ,所以请温柔)

我正在尝试使用以下方法组合函数:

def compose(*functions):
  return functools.reduce(lambda acc, f: lambda x: acc(f(x)), functions, lambda x: x)

对于标量函数,它按预期工作。我想使用返回元组的函数和其他接受多个参数的函数,例如。
def dummy(name):
  return (name, len(name), name.upper())

def transform(name, size, upper):
  return (upper, -size, name)

# What I want to achieve using composition,
# ie. f = compose(transform, dummy)
transform(*dummy('Australia'))
=> ('AUSTRALIA', -9, 'Australia')

由于 dummy 返回一个元组并且 transform 接受三个参数,我需要解压缩该值。

如何使用上面的 compose 函数实现这一点?如果我这样尝试,我会得到:
f = compose(transform, dummy)
f('Australia')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in <lambda>
  File "<stdin>", line 2, in <lambda>
TypeError: transform() takes exactly 3 arguments (1 given)

有没有办法更改 compose 以便它在需要的地方解压?

最佳答案

这个适用于您的示例,但它不会处理任何任意函数 - 它仅适用于位置参数,并且(当然)任何函数的签名必须与前一个(wrt/应用程序顺序)的返回值匹配。

def compose(*functions):
    return functools.reduce(
        lambda f, g: lambda *args: f(*g(*args)),
        functions,
        lambda *args: args
        )

请注意,在此处使用 reduce 虽然在函数式编程中肯定是惯用的,但相当不pythonic。 “明显的”pythonic 实现将使用迭代代替:
def itercompose(*functions):
    def composed(*args):
        for func in reversed(functions):
            args = func(*args)
        return args
    return composed

编辑:

你问“有没有办法让 compose 函数在两种情况下都可以工作”——“两种情况”在这里意味着函数是否返回可迭代的(你称之为“标量函数”,一个没有意义的概念在 Python 中)。

使用基于迭代的实现,您可以只测试返回值是否可迭代并将其包装在一个元组中,即:
import collections

def itercompose(*functions):
    def composed(*args):
        for func in reversed(functions):
            if not isinstance(args, collections.Iterable):
                args = (args,)
            args = func(*args)
        return args
    return composed

但这并不能保证按预期工作 - 实际上,对于大多数用例,这甚至不能按预期工作。 Python 中有很多内置的可迭代类型(甚至更多是用户定义的),仅仅知道一个对象是可迭代的并不能说明它的语义。

例如 dictstr 是可迭代的,但在这种情况下显然应该被视为“标量”。 list 也是可迭代的,在这种情况下应该如何解释它实际上是无法确定的,而无需确切知道它包含什么以及组合顺序中的“下一个”函数期望什么 - 在某些情况下,您可能希望将其视为单个参数, 在其他情况下是 args 列表。

IOW 只有 compose() 函数的调用者才能真正知道应该如何考虑每个函数结果 - 实际上,您甚至可能希望 tuple 被下一个函数视为“标量”值。所以长话短说:不,Python 中没有一刀切的通用解决方案。我能想到的最好的方法是将结果检查和组合函数的手动包装相结合,以便“组合”函数正确解释结果,但此时手动组合函数既简单又强大。

FWIW 记得 Python 首先并且主要是一种动态类型的面向对象语言,因此虽然它确实对函数式编程习语有不错的支持,但它显然不是真正的函数式编程的最佳工具。

关于python - 函数组合、元组和解包,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/47349538/

10-10 18:42
查看更多